Compare commits
3 Commits
94c1aac0ac
...
v2
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2e4e9b12c4 | ||
|
|
2e8cf0e8e7 | ||
|
|
2186c181b5 |
@@ -21,10 +21,5 @@
|
|||||||
**/obj
|
**/obj
|
||||||
**/secrets.dev.yaml
|
**/secrets.dev.yaml
|
||||||
**/values.dev.yaml
|
**/values.dev.yaml
|
||||||
**/appsettings.json
|
|
||||||
**/appsettings.Development.json
|
|
||||||
**/appsettings*
|
|
||||||
**/compose.*
|
|
||||||
**/.env.example
|
|
||||||
LICENSE
|
LICENSE
|
||||||
README.md
|
README.md
|
||||||
10
.env.example
10
.env.example
@@ -1,10 +0,0 @@
|
|||||||
DATABASE_HOST=localhost
|
|
||||||
DATABASE_PORT=5432
|
|
||||||
DATABASE_USERNAME=user
|
|
||||||
DATABASE_PASSWORD=user
|
|
||||||
DATABASE_DATABASE=user
|
|
||||||
OIDC_AUTHORITY=http://localhost:8092
|
|
||||||
OIDC_AUTHORITY=http://localhost:8092
|
|
||||||
OIDC_CLIENT_ID=client_id
|
|
||||||
OIDC_CLIENT_SECRET=client_secret
|
|
||||||
OIDC_REQUIRE_HTTPS_METADATA=false
|
|
||||||
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Auto detect text files and perform LF normalization
|
||||||
|
* text=auto
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
name: "Dev Publish: Nuget"
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- v2.1
|
|
||||||
paths:
|
|
||||||
- 'Moonlight.*/*.csproj'
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
publish:
|
|
||||||
name: Publish Dev Packages
|
|
||||||
|
|
||||||
runs-on: linux_amd64
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Check out repository code
|
|
||||||
uses: actions/checkout@v4
|
|
||||||
|
|
||||||
# Publish api server
|
|
||||||
- name: Build project
|
|
||||||
run: dotnet build Moonlight.Api --configuration Debug
|
|
||||||
|
|
||||||
- name: Publish NuGet package
|
|
||||||
run: dotnet pack Moonlight.Api --configuration Debug --output ./artifacts
|
|
||||||
|
|
||||||
- name: Push nuget package
|
|
||||||
run: dotnet nuget push ./artifacts/*.nupkg --skip-duplicate --source https://git.battlestati.one/api/packages/Moonlight-Panel/nuget/index.json --api-key ${{ secrets.ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Remove artifacts
|
|
||||||
run: rm -rf ./artifacts
|
|
||||||
|
|
||||||
# Publish frontend
|
|
||||||
# We need to build it first so the class list files generate
|
|
||||||
- name: Build project
|
|
||||||
run: dotnet build Hosts/Moonlight.Frontend.Host --configuration Debug
|
|
||||||
|
|
||||||
- name: Build tailwind styles and extract class list
|
|
||||||
working-directory: Hosts/Moonlight.Frontend.Host/Styles
|
|
||||||
run: npm install && npm run build
|
|
||||||
|
|
||||||
- name: Build project
|
|
||||||
run: dotnet build Moonlight.Frontend --configuration Debug
|
|
||||||
|
|
||||||
- name: Publish NuGet package
|
|
||||||
run: dotnet pack Moonlight.Frontend --configuration Debug --output ./artifacts
|
|
||||||
|
|
||||||
- name: Push nuget package
|
|
||||||
run: dotnet nuget push ./artifacts/*.nupkg --skip-duplicate --source https://git.battlestati.one/api/packages/Moonlight-Panel/nuget/index.json --api-key ${{ secrets.ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Remove artifacts
|
|
||||||
run: rm -rf ./artifacts
|
|
||||||
|
|
||||||
# Publish shared
|
|
||||||
- name: Build project
|
|
||||||
run: dotnet build Moonlight.Shared --configuration Debug
|
|
||||||
|
|
||||||
- name: Publish NuGet package
|
|
||||||
run: dotnet pack Moonlight.Shared --configuration Debug --output ./artifacts
|
|
||||||
|
|
||||||
- name: Push nuget package
|
|
||||||
run: dotnet nuget push ./artifacts/*.nupkg --skip-duplicate --source https://git.battlestati.one/api/packages/Moonlight-Panel/nuget/index.json --api-key ${{ secrets.ACCESS_TOKEN }}
|
|
||||||
|
|
||||||
- name: Remove artifacts
|
|
||||||
run: rm -rf ./artifacts
|
|
||||||
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# These are supported funding model platforms
|
||||||
|
ko_fi: masuowo
|
||||||
87
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
87
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
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
|
||||||
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
8
.github/ISSUE_TEMPLATE/config.yml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
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.
|
||||||
32
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
32
.github/ISSUE_TEMPLATE/feature-request.yml
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
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
|
||||||
35
.github/workflows/development-build.yml
vendored
Normal file
35
.github/workflows/development-build.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: Development Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- closed
|
||||||
|
branches: [ "v2" ]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Get current date
|
||||||
|
id: date
|
||||||
|
run: echo "::set-output name=date::$(date +'%Y-%m-%d')"
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login into docker hub
|
||||||
|
run: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PW }}
|
||||||
|
- name: Build and Push Docker image
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Moonlight/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: moonlightpanel/moonlight:dev
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
build-args: |
|
||||||
|
"BUILD_CHANNEL=${{ github.ref_name }}"
|
||||||
|
"BUILD_COMMIT_HASH=${{ github.sha }}"
|
||||||
|
"BUILD_NAME=devbuild ${{ steps.date.outputs.date }}"
|
||||||
|
"BUILD_VERSION=unknown"
|
||||||
35
.github/workflows/release-build.yml
vendored
Normal file
35
.github/workflows/release-build.yml
vendored
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
name: Release Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
inputs:
|
||||||
|
codeName:
|
||||||
|
description: 'Code Name'
|
||||||
|
required: true
|
||||||
|
versionName:
|
||||||
|
description: 'Version Name'
|
||||||
|
required: true
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout code
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
- name: Login into docker hub
|
||||||
|
run: docker login -u ${{ secrets.DOCKER_USER }} -p ${{ secrets.DOCKER_PW }}
|
||||||
|
- name: Build and Push Docker image
|
||||||
|
uses: docker/build-push-action@v5
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./Moonlight/Dockerfile
|
||||||
|
push: true
|
||||||
|
tags: moonlightpanel/moonlight:latest
|
||||||
|
platforms: linux/amd64,linux/arm64
|
||||||
|
build-args: |
|
||||||
|
"BUILD_CHANNEL=${{ github.ref_name }}"
|
||||||
|
"BUILD_COMMIT_HASH=${{ github.sha }}"
|
||||||
|
"BUILD_NAME=${{ github.event.inputs.codeName }}"
|
||||||
|
"BUILD_VERSION=${{ github.event.inputs.versionName }}"
|
||||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -1,4 +1,4 @@
|
|||||||
## Ignore Visual Studio temporary files, build results, and
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
## files generated by popular Visual Studio add-ons.
|
## files generated by popular Visual Studio add-ons.
|
||||||
##
|
##
|
||||||
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
|
||||||
@@ -395,15 +395,10 @@ FodyWeavers.xsd
|
|||||||
*.msp
|
*.msp
|
||||||
|
|
||||||
# JetBrains Rider
|
# JetBrains Rider
|
||||||
**/.idea/**
|
*.sln.iml
|
||||||
|
|
||||||
# Style builds
|
storage/
|
||||||
**/style.min.css
|
.idea/.idea.Moonlight/.idea/dataSources.xml
|
||||||
**/package-lock.json
|
Moonlight/Assets/Core/css/theme.css
|
||||||
**/bun.lock
|
Moonlight/Assets/Core/css/theme.css.map
|
||||||
|
.idea/.idea.Moonlight/.idea/discord.xml
|
||||||
# Secrets
|
|
||||||
**/.env
|
|
||||||
**/appsettings.json
|
|
||||||
**/appsettings.Development.json
|
|
||||||
**/storage
|
|
||||||
|
|||||||
13
.idea/.idea.Moonlight/.idea/.gitignore
generated
vendored
Normal file
13
.idea/.idea.Moonlight/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# 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
|
||||||
12
.idea/.idea.Moonlight/.idea/discord.xml
generated
Normal file
12
.idea/.idea.Moonlight/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?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>
|
||||||
4
.idea/.idea.Moonlight/.idea/encodings.xml
generated
Normal file
4
.idea/.idea.Moonlight/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||||
|
</project>
|
||||||
8
.idea/.idea.Moonlight/.idea/indexLayout.xml
generated
Normal file
8
.idea/.idea.Moonlight/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="UserContentModel">
|
||||||
|
<attachedFolders />
|
||||||
|
<explicitIncludes />
|
||||||
|
<explicitExcludes />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
13
.idea/.idea.Moonlight/.idea/material_theme_project_new.xml
generated
Normal file
13
.idea/.idea.Moonlight/.idea/material_theme_project_new.xml
generated
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
<?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>
|
||||||
6
.idea/.idea.Moonlight/.idea/vcs.xml
generated
Normal file
6
.idea/.idea.Moonlight/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- Put your plugin references here -->
|
|
||||||
<!-- E.g. <PackageReference Include="MoonlightServers.Api" Version="2.1.0" /> -->
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# Base image
|
|
||||||
FROM git.battlestati.one/moonlight-panel/app_base:moonlight AS base
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
|
||||||
|
|
||||||
# Install required packages
|
|
||||||
RUN apt-get update; apt-get install unzip -y; apt-get clean
|
|
||||||
RUN curl -fsSL https://bun.sh/install | bash
|
|
||||||
ENV PATH="/root/.bun/bin:${PATH}"
|
|
||||||
|
|
||||||
# Build options
|
|
||||||
ARG BUILD_CONFIGURATION=Release
|
|
||||||
|
|
||||||
# Download npm packages
|
|
||||||
WORKDIR /src/Hosts/Moonlight.Frontend.Host/Styles
|
|
||||||
|
|
||||||
COPY ["Hosts/Moonlight.Frontend.Host/Styles/package.json", "package.json"]
|
|
||||||
|
|
||||||
RUN bun install
|
|
||||||
|
|
||||||
# Restore nuget packages
|
|
||||||
WORKDIR /src
|
|
||||||
|
|
||||||
COPY ["Moonlight.Api/Moonlight.Api.csproj", "Moonlight.Api/"]
|
|
||||||
COPY ["Moonlight.Frontend/Moonlight.Frontend.csproj", "Moonlight.Frontend/"]
|
|
||||||
COPY ["Moonlight.Shared/Moonlight.Shared.csproj", "Moonlight.Shared/"]
|
|
||||||
|
|
||||||
COPY ["Hosts/Moonlight.Frontend.Host/Moonlight.Frontend.Host.csproj", "Hosts/Moonlight.Frontend.Host/"]
|
|
||||||
COPY ["Hosts/Moonlight.Api.Host/Moonlight.Api.Host.csproj", "Hosts/Moonlight.Api.Host/"]
|
|
||||||
|
|
||||||
COPY ["Hosts/Moonlight.Frontend.Host/Frontend.props", "Hosts/Moonlight.Frontend.Host/"]
|
|
||||||
COPY ["Hosts/Moonlight.Api.Host/Api.props", "Hosts/Moonlight.Api.Host/"]
|
|
||||||
|
|
||||||
RUN dotnet restore "Hosts/Moonlight.Api.Host/Moonlight.Api.Host.csproj"
|
|
||||||
RUN dotnet restore "Hosts/Moonlight.Frontend.Host/Moonlight.Frontend.Host.csproj"
|
|
||||||
|
|
||||||
# Copy over the whole sources
|
|
||||||
COPY . .
|
|
||||||
|
|
||||||
# Build styles
|
|
||||||
# We need to build it before, so the class lists get generated
|
|
||||||
WORKDIR "/src/Hosts/Moonlight.Frontend.Host"
|
|
||||||
RUN dotnet build "./Moonlight.Frontend.Host.csproj" -c $BUILD_CONFIGURATION -o /app/build-frontend
|
|
||||||
|
|
||||||
WORKDIR "/src/Hosts/Moonlight.Frontend.Host/Styles"
|
|
||||||
RUN bun run build
|
|
||||||
|
|
||||||
# Build projects
|
|
||||||
WORKDIR "/src/Hosts/Moonlight.Api.Host"
|
|
||||||
RUN dotnet build "./Moonlight.Api.Host.csproj" -c $BUILD_CONFIGURATION -o /app/build-api
|
|
||||||
|
|
||||||
WORKDIR "/src/Hosts/Moonlight.Frontend.Host"
|
|
||||||
RUN dotnet build "./Moonlight.Frontend.Host.csproj" -c $BUILD_CONFIGURATION -o /app/build-frontend
|
|
||||||
|
|
||||||
FROM build AS publish
|
|
||||||
|
|
||||||
# Publish options
|
|
||||||
ARG BUILD_CONFIGURATION=Release
|
|
||||||
|
|
||||||
# Publish applications
|
|
||||||
WORKDIR "/src/Hosts/Moonlight.Api.Host"
|
|
||||||
RUN dotnet publish "./Moonlight.Api.Host.csproj" -c $BUILD_CONFIGURATION -o /app/publish-api /p:UseAppHost=false
|
|
||||||
|
|
||||||
WORKDIR "/src/Hosts/Moonlight.Frontend.Host"
|
|
||||||
RUN dotnet publish "./Moonlight.Frontend.Host.csproj" -c $BUILD_CONFIGURATION -o /app/publish-frontend /p:UseAppHost=false
|
|
||||||
|
|
||||||
FROM base AS final
|
|
||||||
|
|
||||||
WORKDIR /app
|
|
||||||
|
|
||||||
COPY --from=publish /app/publish-api .
|
|
||||||
COPY --from=publish /app/publish-frontend/wwwroot ./wwwroot
|
|
||||||
|
|
||||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=10s --retries=3 CMD ["/usr/bin/curl", "-sf", "-o", "/dev/null", "http://localhost:8080/"]
|
|
||||||
|
|
||||||
ENTRYPOINT ["dotnet", "Moonlight.Api.Host.dll"]
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.1">
|
|
||||||
<PrivateAssets>all</PrivateAssets>
|
|
||||||
<IncludeAssets>runtime; build; native; analyzers; buildtransitive</IncludeAssets>
|
|
||||||
</PackageReference>
|
|
||||||
|
|
||||||
<PackageReference Include="SimplePlugin" Version="1.0.2" />
|
|
||||||
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Moonlight.Api\Moonlight.Api.csproj"/>
|
|
||||||
<ProjectReference Include="..\Moonlight.Frontend.Host\Moonlight.Frontend.Host.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Content Include="..\..\.dockerignore">
|
|
||||||
<Link>.dockerignore</Link>
|
|
||||||
</Content>
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Import Project="Api.props"/>
|
|
||||||
</Project>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Moonlight.Api;
|
|
||||||
using SimplePlugin.Generated;
|
|
||||||
|
|
||||||
var plugins = PluginRegistry
|
|
||||||
.Modules
|
|
||||||
.OfType<MoonlightPlugin>()
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
await StartupHandler.RunAsync(args, plugins);
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
<Project>
|
|
||||||
<ItemGroup>
|
|
||||||
<!-- Put your plugin references here -->
|
|
||||||
<!-- E.g. <PackageReference Include="MoonlightServers.Frontend" Version="2.1.0" /> -->
|
|
||||||
</ItemGroup>
|
|
||||||
</Project>
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk.BlazorWebAssembly">
|
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetFramework>net10.0</TargetFramework>
|
|
||||||
<Nullable>enable</Nullable>
|
|
||||||
<ImplicitUsings>enable</ImplicitUsings>
|
|
||||||
|
|
||||||
<StaticWebAssetsEnabled>True</StaticWebAssetsEnabled>
|
|
||||||
<OverrideHtmlAssetPlaceholders>true</OverrideHtmlAssetPlaceholders>
|
|
||||||
<WasmEnableSIMD>true</WasmEnableSIMD>
|
|
||||||
<PublishTrimmed>true</PublishTrimmed>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1"/>
|
|
||||||
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.1" PrivateAssets="all"/>
|
|
||||||
<PackageReference Include="SimplePlugin" Version="1.0.2" />
|
|
||||||
<PackageReference Include="SimplePlugin.Abstractions" Version="1.0.2" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<ProjectReference Include="..\..\Moonlight.Frontend\Moonlight.Frontend.csproj" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<Import Project="Frontend.props"/>
|
|
||||||
</Project>
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Moonlight.Frontend;
|
|
||||||
using SimplePlugin.Generated;
|
|
||||||
|
|
||||||
var plugins = PluginRegistry
|
|
||||||
.Modules
|
|
||||||
.OfType<MoonlightPlugin>()
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
await StartupHandler.RunAsync(args, plugins);
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/launchsettings.json",
|
|
||||||
"profiles": {
|
|
||||||
"http": {
|
|
||||||
"commandName": "Project",
|
|
||||||
"dotnetRunMessages": true,
|
|
||||||
"launchBrowser": true,
|
|
||||||
"inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
|
|
||||||
"applicationUrl": "http://localhost:5250",
|
|
||||||
"environmentVariables": {
|
|
||||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
import fs from 'fs';
|
|
||||||
import selectorParser from 'postcss-selector-parser';
|
|
||||||
|
|
||||||
export default function extractTailwindClasses(opts = {}) {
|
|
||||||
const classSet = new Set();
|
|
||||||
|
|
||||||
return {
|
|
||||||
postcssPlugin: 'extract-tailwind-classes',
|
|
||||||
Rule(rule) {
|
|
||||||
selectorParser(selectors => {
|
|
||||||
selectors.walkClasses(node => {
|
|
||||||
classSet.add(node.value);
|
|
||||||
});
|
|
||||||
}).processSync(rule.selector);
|
|
||||||
},
|
|
||||||
OnceExit() {
|
|
||||||
const classArray = Array.from(classSet).sort();
|
|
||||||
fs.mkdirSync('../../../Moonlight.Frontend/Styles', { recursive: true });
|
|
||||||
fs.writeFileSync('../../../Moonlight.Frontend/Styles/Moonlight.Frontend.map', classArray.join('\n'));
|
|
||||||
console.log(`Extracted classes ${classArray.length}`);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
extractTailwindClasses.postcss = true;
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"scripts": {
|
|
||||||
"dev": "npx postcss styles.css -o ../wwwroot/style.min.css --watch",
|
|
||||||
"dev-build": "npx postcss styles.css -o ../wwwroot/style.min.css",
|
|
||||||
"build": "cross-env EXTRACT_CLASSES=true npx postcss styles.css -o ../wwwroot/style.min.css"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@tailwindcss/postcss": "^4.1.18",
|
|
||||||
"cssnano": "^7.1.2",
|
|
||||||
"postcss": "^8.5.6",
|
|
||||||
"postcss-cli": "^11.0.1",
|
|
||||||
"postcss-selector-parser": "^7.1.1",
|
|
||||||
"tailwindcss": "^4.1.18",
|
|
||||||
"tw-animate-css": "^1.4.0"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"cross-env": "^10.1.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
import tailwindcss from '@tailwindcss/postcss';
|
|
||||||
import cssnano from 'cssnano';
|
|
||||||
import extractTailwindClasses from './extract-classes.mjs';
|
|
||||||
|
|
||||||
const plugins = [
|
|
||||||
tailwindcss,
|
|
||||||
cssnano({
|
|
||||||
preset: 'default'
|
|
||||||
})
|
|
||||||
];
|
|
||||||
|
|
||||||
if (process.env.EXTRACT_CLASSES === "true") {
|
|
||||||
plugins.push(extractTailwindClasses());
|
|
||||||
}
|
|
||||||
|
|
||||||
export default {
|
|
||||||
plugins
|
|
||||||
};
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
@import "tailwindcss";
|
|
||||||
@import "tw-animate-css";
|
|
||||||
|
|
||||||
@import "../bin/ShadcnBlazor/scrollbar.css";
|
|
||||||
@import "../bin/ShadcnBlazor/default-theme.css";
|
|
||||||
@import "./theme.css";
|
|
||||||
|
|
||||||
@source "../bin/ShadcnBlazor/ShadcnBlazor.map";
|
|
||||||
|
|
||||||
@source "../../../Moonlight.Api/**/*.razor";
|
|
||||||
@source "../../../Moonlight.Api/**/*.cs";
|
|
||||||
@source "../../../Moonlight.Api/**/*.html";
|
|
||||||
|
|
||||||
@source "../../../Moonlight.Frontend/**/*.razor";
|
|
||||||
@source "../../../Moonlight.Frontend/**/*.cs";
|
|
||||||
@source "../../../Moonlight.Frontend/**/*.html";
|
|
||||||
|
|
||||||
@custom-variant dark (&:is(.dark *));
|
|
||||||
|
|
||||||
#blazor-error-ui {
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@layer base {
|
|
||||||
* {
|
|
||||||
@apply border-border outline-ring/50;
|
|
||||||
}
|
|
||||||
body {
|
|
||||||
@apply bg-background text-foreground;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,132 +0,0 @@
|
|||||||
:root {
|
|
||||||
--radius: 0.625rem;
|
|
||||||
--background: oklch(1 0 0);
|
|
||||||
--foreground: oklch(0.129 0.042 264.695);
|
|
||||||
--card: oklch(1 0 0);
|
|
||||||
--card-foreground: oklch(0.129 0.042 264.695);
|
|
||||||
--popover: oklch(1 0 0);
|
|
||||||
--popover-foreground: oklch(0.129 0.042 264.695);
|
|
||||||
--primary: oklch(0.208 0.042 265.755);
|
|
||||||
--primary-foreground: oklch(0.984 0.003 247.858);
|
|
||||||
--secondary: oklch(0.968 0.007 247.896);
|
|
||||||
--secondary-foreground: oklch(0.208 0.042 265.755);
|
|
||||||
--muted: oklch(0.968 0.007 247.896);
|
|
||||||
--muted-foreground: oklch(0.554 0.046 257.417);
|
|
||||||
--accent: oklch(0.968 0.007 247.896);
|
|
||||||
--accent-foreground: oklch(0.208 0.042 265.755);
|
|
||||||
--destructive: oklch(0.577 0.245 27.325);
|
|
||||||
--border: oklch(0.929 0.013 255.508);
|
|
||||||
--input: oklch(0.929 0.013 255.508);
|
|
||||||
--ring: oklch(0.704 0.04 256.788);
|
|
||||||
--chart-1: oklch(0.646 0.222 41.116);
|
|
||||||
--chart-2: oklch(0.6 0.118 184.704);
|
|
||||||
--chart-3: oklch(0.398 0.07 227.392);
|
|
||||||
--chart-4: oklch(0.828 0.189 84.429);
|
|
||||||
--chart-5: oklch(0.769 0.188 70.08);
|
|
||||||
|
|
||||||
--sidebar: var(--background);
|
|
||||||
--sidebar-foreground: var(--foreground);
|
|
||||||
--sidebar-primary: var(--primary);
|
|
||||||
--sidebar-primary-foreground: var(--primary-foreground);
|
|
||||||
--sidebar-accent: var(--accent);
|
|
||||||
--sidebar-accent-foreground: var(--accent-foreground);
|
|
||||||
--sidebar-border: var(--border);
|
|
||||||
--sidebar-ring: var(--ring);
|
|
||||||
|
|
||||||
--font-sans: Inter, sans-serif;
|
|
||||||
--font-serif: Georgia, serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
/* Deep blue-slate background with purple undertones */
|
|
||||||
--background: oklch(0.16 0.028 260);
|
|
||||||
--foreground: oklch(0.98 0.008 260);
|
|
||||||
|
|
||||||
/* Cards with slightly lighter blue-slate */
|
|
||||||
--card: oklch(0.21 0.032 260);
|
|
||||||
--card-foreground: oklch(0.98 0.008 260);
|
|
||||||
|
|
||||||
/* Popovers with medium depth */
|
|
||||||
--popover: oklch(0.24 0.035 260);
|
|
||||||
--popover-foreground: oklch(0.98 0.008 260);
|
|
||||||
|
|
||||||
/* Vibrant blue-purple primary */
|
|
||||||
--primary: oklch(0.62 0.18 270);
|
|
||||||
--primary-foreground: oklch(0.99 0.005 260);
|
|
||||||
|
|
||||||
/* Secondary with blue-slate tone */
|
|
||||||
--secondary: oklch(0.27 0.038 260);
|
|
||||||
--secondary-foreground: oklch(0.98 0.008 260);
|
|
||||||
|
|
||||||
/* Muted elements */
|
|
||||||
--muted: oklch(0.25 0.035 260);
|
|
||||||
--muted-foreground: oklch(0.66 0.025 260);
|
|
||||||
|
|
||||||
/* Accent with purple-blue blend */
|
|
||||||
--accent: oklch(0.36 0.065 268);
|
|
||||||
--accent-foreground: oklch(0.98 0.008 260);
|
|
||||||
|
|
||||||
/* Destructive red with good contrast */
|
|
||||||
--destructive: oklch(0.62 0.22 25);
|
|
||||||
--destructive-foreground: oklch(0.99 0.005 260);
|
|
||||||
|
|
||||||
/* Subtle borders and inputs */
|
|
||||||
--border: oklch(0.32 0.025 260);
|
|
||||||
--input: oklch(0.30 0.030 260);
|
|
||||||
--ring: oklch(0.62 0.18 270);
|
|
||||||
|
|
||||||
/* Chart colors with blue-purple harmony */
|
|
||||||
--chart-1: oklch(0.58 0.18 270);
|
|
||||||
--chart-2: oklch(0.62 0.16 245);
|
|
||||||
--chart-3: oklch(0.68 0.15 290);
|
|
||||||
--chart-4: oklch(0.60 0.20 260);
|
|
||||||
--chart-5: oklch(0.65 0.14 280);
|
|
||||||
|
|
||||||
/* Sidebar with slightly different depth */
|
|
||||||
--sidebar: oklch(0.18 0.030 260);
|
|
||||||
--sidebar-foreground: oklch(0.97 0.008 260);
|
|
||||||
--sidebar-primary: oklch(0.60 0.17 270);
|
|
||||||
--sidebar-primary-foreground: oklch(0.99 0.005 260);
|
|
||||||
--sidebar-accent: oklch(0.26 0.038 260);
|
|
||||||
--sidebar-accent-foreground: oklch(0.98 0.008 260);
|
|
||||||
--sidebar-border: oklch(0.30 0.025 260);
|
|
||||||
--sidebar-ring: oklch(0.58 0.17 270);
|
|
||||||
}
|
|
||||||
|
|
||||||
@theme inline {
|
|
||||||
--radius-sm: calc(var(--radius) - 4px);
|
|
||||||
--radius-md: calc(var(--radius) - 2px);
|
|
||||||
--radius-lg: var(--radius);
|
|
||||||
--radius-xl: calc(var(--radius) + 4px);
|
|
||||||
--color-background: var(--background);
|
|
||||||
--color-foreground: var(--foreground);
|
|
||||||
--color-card: var(--card);
|
|
||||||
--color-card-foreground: var(--card-foreground);
|
|
||||||
--color-popover: var(--popover);
|
|
||||||
--color-popover-foreground: var(--popover-foreground);
|
|
||||||
--color-primary: var(--primary);
|
|
||||||
--color-primary-foreground: var(--primary-foreground);
|
|
||||||
--color-secondary: var(--secondary);
|
|
||||||
--color-secondary-foreground: var(--secondary-foreground);
|
|
||||||
--color-muted: var(--muted);
|
|
||||||
--color-muted-foreground: var(--muted-foreground);
|
|
||||||
--color-accent: var(--accent);
|
|
||||||
--color-accent-foreground: var(--accent-foreground);
|
|
||||||
--color-destructive: var(--destructive);
|
|
||||||
--color-border: var(--border);
|
|
||||||
--color-input: var(--input);
|
|
||||||
--color-ring: var(--ring);
|
|
||||||
--color-chart-1: var(--chart-1);
|
|
||||||
--color-chart-2: var(--chart-2);
|
|
||||||
--color-chart-3: var(--chart-3);
|
|
||||||
--color-chart-4: var(--chart-4);
|
|
||||||
--color-chart-5: var(--chart-5);
|
|
||||||
--color-sidebar: var(--sidebar);
|
|
||||||
--color-sidebar-foreground: var(--sidebar-foreground);
|
|
||||||
--color-sidebar-primary: var(--sidebar-primary);
|
|
||||||
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
|
|
||||||
--color-sidebar-accent: var(--sidebar-accent);
|
|
||||||
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
|
|
||||||
--color-sidebar-border: var(--sidebar-border);
|
|
||||||
--color-sidebar-ring: var(--sidebar-ring);
|
|
||||||
}
|
|
||||||
@@ -1,108 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html lang="en" class="dark">
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
||||||
<title>Moonlight</title>
|
|
||||||
<base href="/" />
|
|
||||||
<link rel="preload" id="webassembly" />
|
|
||||||
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=fallback" />
|
|
||||||
<link rel="stylesheet" href="style.min.css" />
|
|
||||||
<script type="importmap"></script>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window.frontendConfig = {
|
|
||||||
STYLE_TAG_ID: 'theme-variables',
|
|
||||||
configuration: {},
|
|
||||||
|
|
||||||
applyTheme: function(cssContent) {
|
|
||||||
// Find or create the style tag
|
|
||||||
let styleTag = document.getElementById(this.STYLE_TAG_ID);
|
|
||||||
|
|
||||||
if (!styleTag) {
|
|
||||||
styleTag = document.createElement('style');
|
|
||||||
styleTag.id = this.STYLE_TAG_ID;
|
|
||||||
document.head.appendChild(styleTag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Update the style tag content
|
|
||||||
styleTag.textContent = cssContent;
|
|
||||||
},
|
|
||||||
|
|
||||||
reloadConfiguration: function (){
|
|
||||||
try {
|
|
||||||
const xhr = new XMLHttpRequest();
|
|
||||||
xhr.open('GET', '/api/frontend/config', false);
|
|
||||||
xhr.send(null);
|
|
||||||
|
|
||||||
if (xhr.status === 200) {
|
|
||||||
this.configuration = JSON.parse(xhr.responseText);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Failed to load initial theme:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
getConfiguration: function (){
|
|
||||||
return this.configuration;
|
|
||||||
},
|
|
||||||
|
|
||||||
reload: function () {
|
|
||||||
this.reloadConfiguration();
|
|
||||||
|
|
||||||
document.title = this.configuration.name;
|
|
||||||
this.applyTheme(this.configuration.themeCss);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
window.frontendConfig.reload();
|
|
||||||
</script>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="bg-background text-foreground">
|
|
||||||
<div id="app">
|
|
||||||
<div class="h-screen w-full flex items-center justify-center">
|
|
||||||
|
|
||||||
<div class="flex min-w-0 flex-1 flex-col items-center justify-center gap-3 rounded-lg border-dashed p-6 text-center text-balance md:p-12">
|
|
||||||
<div class="flex max-w-sm flex-col items-center gap-2 text-center">
|
|
||||||
<div class="flex shrink-0 items-center justify-center [&_svg]:pointer-events-none [&_svg]:shrink-0 bg-muted text-foreground size-10 rounded-lg [&_svg:not([class*='size-'])]:size-6">
|
|
||||||
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none"
|
|
||||||
stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"
|
|
||||||
class="lucide lucide-zap-icon lucide-zap size-6">
|
|
||||||
<path d="M4 14a1 1 0 0 1-.78-1.63l9.9-10.2a.5.5 0 0 1 .86.46l-1.92 6.02A1 1 0 0 0 13 10h7a1 1 0 0 1 .78 1.63l-9.9 10.2a.5.5 0 0 1-.86-.46l1.92-6.02A1 1 0 0 0 11 14z"/>
|
|
||||||
</svg>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="text-lg font-medium tracking-tight">
|
|
||||||
Loading application
|
|
||||||
</div>
|
|
||||||
<div class="flex w-full max-w-sm min-w-0 flex-col items-center gap-4 text-sm text-balance">
|
|
||||||
|
|
||||||
<div class="bg-primary/20 w-full relative h-2 overflow-hidden rounded-full">
|
|
||||||
<div class="bg-primary h-full w-[var(--blazor-load-percentage,0)] flex-1 transition-all">
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div id="blazor-error-ui">
|
|
||||||
An unhandled error has occurred.
|
|
||||||
<a href="." class="reload">Reload</a>
|
|
||||||
<span class="dismiss">🗙</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="/_content/ShadcnBlazor/interop.js" defer></script>
|
|
||||||
<script src="/_content/ShadcnBlazor.Extras/interop.js" defer></script>
|
|
||||||
<script src="/_content/ShadcnBlazor.Extras/codemirror-bundle.js" defer></script>
|
|
||||||
<script src="_framework/blazor.webassembly#[.{fingerprint}].js"></script>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
||||||
121
LICENSE
Normal file
121
LICENSE
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
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.
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class ApiOptions
|
|
||||||
{
|
|
||||||
public TimeSpan LookupCacheL1Expiry { get; set; } = TimeSpan.FromSeconds(30);
|
|
||||||
public TimeSpan LookupCacheL2Expiry { get; set; } = TimeSpan.FromMinutes(3);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class CacheOptions
|
|
||||||
{
|
|
||||||
public bool EnableLayer2 { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class ContainerHelperOptions
|
|
||||||
{
|
|
||||||
public bool IsEnabled { get; set; }
|
|
||||||
public string Url { get; set; } = "http://helper:8080";
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class DatabaseOptions
|
|
||||||
{
|
|
||||||
public string Host { get; set; }
|
|
||||||
public int Port { get; set; } = 5432;
|
|
||||||
public string Username { get; set; }
|
|
||||||
public string Password { get; set; }
|
|
||||||
public string Database { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class FrontendOptions
|
|
||||||
{
|
|
||||||
public bool Enabled { get; set; } = true;
|
|
||||||
public int CacheMinutes { get; set; } = 3;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class OidcOptions
|
|
||||||
{
|
|
||||||
public string Authority { get; set; }
|
|
||||||
public bool RequireHttpsMetadata { get; set; } = true;
|
|
||||||
public bool DisableHttpsOnlyCookies { get; set; }
|
|
||||||
public string ResponseType { get; set; } = "code";
|
|
||||||
public string[]? Scopes { get; set; }
|
|
||||||
public string ClientId { get; set; }
|
|
||||||
public string ClientSecret { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class RedisOptions
|
|
||||||
{
|
|
||||||
public bool Enable { get; set; }
|
|
||||||
public string ConnectionString { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class SettingsOptions
|
|
||||||
{
|
|
||||||
public TimeSpan LookupL1CacheTime { get; set; } = TimeSpan.FromMinutes(1);
|
|
||||||
public TimeSpan LookupL2CacheTime { get; set; } = TimeSpan.FromMinutes(5);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class UserOptions
|
|
||||||
{
|
|
||||||
public TimeSpan ValidationCacheL1Expiry { get; set; } = TimeSpan.FromSeconds(30);
|
|
||||||
public TimeSpan ValidationCacheL2Expiry { get; set; } = TimeSpan.FromMinutes(3);
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Configuration;
|
|
||||||
|
|
||||||
public class VersionOptions
|
|
||||||
{
|
|
||||||
public bool OfflineMode { get; set; }
|
|
||||||
public string? CurrentOverride { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,44 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Moonlight.Api.Configuration;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database;
|
|
||||||
|
|
||||||
public class DataContext : DbContext
|
|
||||||
{
|
|
||||||
public DbSet<User> Users { get; set; }
|
|
||||||
public DbSet<SettingsOption> SettingsOptions { get; set; }
|
|
||||||
public DbSet<Role> Roles { get; set; }
|
|
||||||
public DbSet<RoleMember> RoleMembers { get; set; }
|
|
||||||
public DbSet<ApiKey> ApiKeys { get; set; }
|
|
||||||
public DbSet<Theme> Themes { get; set; }
|
|
||||||
|
|
||||||
private readonly IOptions<DatabaseOptions> Options;
|
|
||||||
|
|
||||||
public DataContext(IOptions<DatabaseOptions> options)
|
|
||||||
{
|
|
||||||
Options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
|
|
||||||
{
|
|
||||||
if (optionsBuilder.IsConfigured)
|
|
||||||
return;
|
|
||||||
|
|
||||||
optionsBuilder.UseNpgsql(
|
|
||||||
$"Host={Options.Value.Host};" +
|
|
||||||
$"Port={Options.Value.Port};" +
|
|
||||||
$"Username={Options.Value.Username};" +
|
|
||||||
$"Password={Options.Value.Password};" +
|
|
||||||
$"Database={Options.Value.Database}"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
modelBuilder.HasDefaultSchema("core");
|
|
||||||
|
|
||||||
base.OnModelCreating(modelBuilder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database.Interfaces;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database;
|
|
||||||
|
|
||||||
public class DatabaseRepository<T> where T : class
|
|
||||||
{
|
|
||||||
private readonly DataContext DataContext;
|
|
||||||
private readonly DbSet<T> Set;
|
|
||||||
|
|
||||||
public DatabaseRepository(DataContext dataContext)
|
|
||||||
{
|
|
||||||
DataContext = dataContext;
|
|
||||||
Set = DataContext.Set<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public IQueryable<T> Query() => Set;
|
|
||||||
|
|
||||||
public async Task<T> AddAsync(T entity)
|
|
||||||
{
|
|
||||||
if (entity is IActionTimestamps actionTimestamps)
|
|
||||||
{
|
|
||||||
actionTimestamps.CreatedAt = DateTimeOffset.UtcNow;
|
|
||||||
actionTimestamps.UpdatedAt = DateTimeOffset.UtcNow;
|
|
||||||
}
|
|
||||||
|
|
||||||
var final = Set.Add(entity);
|
|
||||||
await DataContext.SaveChangesAsync();
|
|
||||||
return final.Entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task UpdateAsync(T entity)
|
|
||||||
{
|
|
||||||
if (entity is IActionTimestamps actionTimestamps)
|
|
||||||
actionTimestamps.UpdatedAt = DateTimeOffset.UtcNow;
|
|
||||||
|
|
||||||
Set.Update(entity);
|
|
||||||
await DataContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task RemoveAsync(T entity)
|
|
||||||
{
|
|
||||||
Set.Remove(entity);
|
|
||||||
await DataContext.SaveChangesAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Moonlight.Api.Database.Interfaces;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
public class ApiKey : IActionTimestamps
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(30)]
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(300)]
|
|
||||||
public required string Description { get; set; }
|
|
||||||
|
|
||||||
public string[] Permissions { get; set; } = [];
|
|
||||||
public DateTimeOffset ValidUntil { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(32)]
|
|
||||||
public string Key { get; set; }
|
|
||||||
|
|
||||||
// Action timestamps
|
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
|
||||||
public DateTimeOffset UpdatedAt { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Moonlight.Api.Database.Interfaces;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
public class Role : IActionTimestamps
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(30)]
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(300)]
|
|
||||||
public required string Description { get; set; }
|
|
||||||
|
|
||||||
public string[] Permissions { get; set; } = [];
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
public List<RoleMember> Members { get; set; } = [];
|
|
||||||
|
|
||||||
// Action timestamps
|
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
|
||||||
public DateTimeOffset UpdatedAt { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
using Moonlight.Api.Database.Interfaces;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
public class RoleMember : IActionTimestamps
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
public Role Role { get; set; }
|
|
||||||
public User User { get; set; }
|
|
||||||
|
|
||||||
// Action timestamps
|
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
|
||||||
public DateTimeOffset UpdatedAt { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using System.ComponentModel.DataAnnotations.Schema;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
public class SettingsOption
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(256)]
|
|
||||||
public required string Key { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(4096)]
|
|
||||||
[Column(TypeName = "jsonb")]
|
|
||||||
public required string ValueJson { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
public class Theme
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(30)]
|
|
||||||
public required string Name { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(30)]
|
|
||||||
public required string Version { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(30)]
|
|
||||||
public required string Author { get; set; }
|
|
||||||
public bool IsEnabled { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(20_000)]
|
|
||||||
public required string CssContent { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
using System.ComponentModel.DataAnnotations;
|
|
||||||
using Moonlight.Api.Database.Interfaces;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
public class User : IActionTimestamps
|
|
||||||
{
|
|
||||||
public int Id { get; set; }
|
|
||||||
|
|
||||||
// Base information
|
|
||||||
[MaxLength(50)]
|
|
||||||
public required string Username { get; set; }
|
|
||||||
|
|
||||||
[MaxLength(254)]
|
|
||||||
public required string Email { get; set; }
|
|
||||||
|
|
||||||
// Authentication
|
|
||||||
public DateTimeOffset InvalidateTimestamp { get; set; }
|
|
||||||
|
|
||||||
// Relations
|
|
||||||
public List<RoleMember> RoleMemberships { get; set; } = [];
|
|
||||||
|
|
||||||
// Action timestamps
|
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
|
||||||
public DateTimeOffset UpdatedAt { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Moonlight.Api.Database.Interfaces;
|
|
||||||
|
|
||||||
internal interface IActionTimestamps
|
|
||||||
{
|
|
||||||
public DateTimeOffset CreatedAt { get; set; }
|
|
||||||
public DateTimeOffset UpdatedAt { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20251225202335_AddedUsersAndSettings")]
|
|
||||||
partial class AddedUsersAndSettings
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("character varying(4096)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedUsersAndSettings : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.EnsureSchema(
|
|
||||||
name: "core");
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "SettingsOptions",
|
|
||||||
schema: "core",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Key = table.Column<string>(type: "character varying(256)", maxLength: 256, nullable: false),
|
|
||||||
Value = table.Column<string>(type: "character varying(4096)", maxLength: 4096, nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_SettingsOptions", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Users",
|
|
||||||
schema: "core",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Username = table.Column<string>(type: "character varying(50)", maxLength: 50, nullable: false),
|
|
||||||
Email = table.Column<string>(type: "character varying(254)", maxLength: 254, nullable: false),
|
|
||||||
InvalidateTimestamp = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Users", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "SettingsOptions",
|
|
||||||
schema: "core");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Users",
|
|
||||||
schema: "core");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,177 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20251230200748_AddedRolesAndActionTimestamps")]
|
|
||||||
partial class AddedRolesAndActionTimestamps
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(15)
|
|
||||||
.HasColumnType("character varying(15)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("character varying(4096)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,115 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedRolesAndActionTimestamps : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<DateTimeOffset>(
|
|
||||||
name: "CreatedAt",
|
|
||||||
schema: "core",
|
|
||||||
table: "Users",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<DateTimeOffset>(
|
|
||||||
name: "UpdatedAt",
|
|
||||||
schema: "core",
|
|
||||||
table: "Users",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Roles",
|
|
||||||
schema: "core",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Name = table.Column<string>(type: "character varying(15)", maxLength: 15, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
|
||||||
Permissions = table.Column<string[]>(type: "text[]", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Roles", x => x.Id);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "RoleMembers",
|
|
||||||
schema: "core",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
RoleId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
UserId = table.Column<int>(type: "integer", nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_RoleMembers", x => x.Id);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_RoleMembers_Roles_RoleId",
|
|
||||||
column: x => x.RoleId,
|
|
||||||
principalSchema: "core",
|
|
||||||
principalTable: "Roles",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
table.ForeignKey(
|
|
||||||
name: "FK_RoleMembers_Users_UserId",
|
|
||||||
column: x => x.UserId,
|
|
||||||
principalSchema: "core",
|
|
||||||
principalTable: "Users",
|
|
||||||
principalColumn: "Id",
|
|
||||||
onDelete: ReferentialAction.Cascade);
|
|
||||||
});
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_RoleMembers_RoleId",
|
|
||||||
schema: "core",
|
|
||||||
table: "RoleMembers",
|
|
||||||
column: "RoleId");
|
|
||||||
|
|
||||||
migrationBuilder.CreateIndex(
|
|
||||||
name: "IX_RoleMembers_UserId",
|
|
||||||
schema: "core",
|
|
||||||
table: "RoleMembers",
|
|
||||||
column: "UserId");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "RoleMembers",
|
|
||||||
schema: "core");
|
|
||||||
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Roles",
|
|
||||||
schema: "core");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "CreatedAt",
|
|
||||||
schema: "core",
|
|
||||||
table: "Users");
|
|
||||||
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "UpdatedAt",
|
|
||||||
schema: "core",
|
|
||||||
table: "Users");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20260116133404_AddedApiKeys")]
|
|
||||||
partial class AddedApiKeys
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(15)
|
|
||||||
.HasColumnType("character varying(15)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("ApiKeys", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(100)
|
|
||||||
.HasColumnType("character varying(100)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(15)
|
|
||||||
.HasColumnType("character varying(15)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("character varying(4096)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedApiKeys : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "ApiKeys",
|
|
||||||
schema: "core",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Name = table.Column<string>(type: "character varying(15)", maxLength: 15, nullable: false),
|
|
||||||
Description = table.Column<string>(type: "character varying(100)", maxLength: 100, nullable: false),
|
|
||||||
Permissions = table.Column<string[]>(type: "text[]", nullable: false),
|
|
||||||
Key = table.Column<string>(type: "character varying(32)", maxLength: 32, nullable: false),
|
|
||||||
CreatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false),
|
|
||||||
UpdatedAt = table.Column<DateTimeOffset>(type: "timestamp with time zone", nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_ApiKeys", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "ApiKeys",
|
|
||||||
schema: "core");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,215 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20260116134322_AdjustedLenghtsOfRoleAndApiKeyStrings")]
|
|
||||||
partial class AdjustedLenghtsOfRoleAndApiKeyStrings
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("ApiKeys", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("character varying(4096)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AdjustedLenghtsOfRoleAndApiKeyStrings : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
schema: "core",
|
|
||||||
table: "Roles",
|
|
||||||
type: "character varying(30)",
|
|
||||||
maxLength: 30,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(15)",
|
|
||||||
oldMaxLength: 15);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Description",
|
|
||||||
schema: "core",
|
|
||||||
table: "Roles",
|
|
||||||
type: "character varying(300)",
|
|
||||||
maxLength: 300,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(100)",
|
|
||||||
oldMaxLength: 100);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
schema: "core",
|
|
||||||
table: "ApiKeys",
|
|
||||||
type: "character varying(30)",
|
|
||||||
maxLength: 30,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(15)",
|
|
||||||
oldMaxLength: 15);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Description",
|
|
||||||
schema: "core",
|
|
||||||
table: "ApiKeys",
|
|
||||||
type: "character varying(300)",
|
|
||||||
maxLength: 300,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(100)",
|
|
||||||
oldMaxLength: 100);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
schema: "core",
|
|
||||||
table: "Roles",
|
|
||||||
type: "character varying(15)",
|
|
||||||
maxLength: 15,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(30)",
|
|
||||||
oldMaxLength: 30);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Description",
|
|
||||||
schema: "core",
|
|
||||||
table: "Roles",
|
|
||||||
type: "character varying(100)",
|
|
||||||
maxLength: 100,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(300)",
|
|
||||||
oldMaxLength: 300);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Name",
|
|
||||||
schema: "core",
|
|
||||||
table: "ApiKeys",
|
|
||||||
type: "character varying(15)",
|
|
||||||
maxLength: 15,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(30)",
|
|
||||||
oldMaxLength: 30);
|
|
||||||
|
|
||||||
migrationBuilder.AlterColumn<string>(
|
|
||||||
name: "Description",
|
|
||||||
schema: "core",
|
|
||||||
table: "ApiKeys",
|
|
||||||
type: "character varying(100)",
|
|
||||||
maxLength: 100,
|
|
||||||
nullable: false,
|
|
||||||
oldClrType: typeof(string),
|
|
||||||
oldType: "character varying(300)",
|
|
||||||
oldMaxLength: 300);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20260118005634_AddedThemes")]
|
|
||||||
partial class AddedThemes
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("ApiKeys", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("Value")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("character varying(4096)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Theme", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("CssContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(20000)
|
|
||||||
.HasColumnType("character varying(20000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsEnabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Themes", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedThemes : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.CreateTable(
|
|
||||||
name: "Themes",
|
|
||||||
schema: "core",
|
|
||||||
columns: table => new
|
|
||||||
{
|
|
||||||
Id = table.Column<int>(type: "integer", nullable: false)
|
|
||||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
|
||||||
Name = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
|
||||||
Version = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
|
||||||
Author = table.Column<string>(type: "character varying(30)", maxLength: 30, nullable: false),
|
|
||||||
IsEnabled = table.Column<bool>(type: "boolean", nullable: false),
|
|
||||||
CssContent = table.Column<string>(type: "character varying(20000)", maxLength: 20000, nullable: false)
|
|
||||||
},
|
|
||||||
constraints: table =>
|
|
||||||
{
|
|
||||||
table.PrimaryKey("PK_Themes", x => x.Id);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropTable(
|
|
||||||
name: "Themes",
|
|
||||||
schema: "core");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20260129134620_SwitchedToJsonForSettingsOption")]
|
|
||||||
partial class SwitchedToJsonForSettingsOption
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("ApiKeys", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("ValueJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Theme", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("CssContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(20000)
|
|
||||||
.HasColumnType("character varying(20000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsEnabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Themes", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class SwitchedToJsonForSettingsOption : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "Value",
|
|
||||||
schema: "core",
|
|
||||||
table: "SettingsOptions");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "ValueJson",
|
|
||||||
schema: "core",
|
|
||||||
table: "SettingsOptions",
|
|
||||||
type: "jsonb",
|
|
||||||
maxLength: 4096,
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "ValueJson",
|
|
||||||
schema: "core",
|
|
||||||
table: "SettingsOptions");
|
|
||||||
|
|
||||||
migrationBuilder.AddColumn<string>(
|
|
||||||
name: "Value",
|
|
||||||
schema: "core",
|
|
||||||
table: "SettingsOptions",
|
|
||||||
type: "character varying(4096)",
|
|
||||||
maxLength: 4096,
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: "");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,254 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
[Migration("20260209114238_AddedValidUntilToApiKeys")]
|
|
||||||
partial class AddedValidUntilToApiKeys
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("ValidUntil")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("ApiKeys", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("ValueJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Theme", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("CssContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(20000)
|
|
||||||
.HasColumnType("character varying(20000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsEnabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Themes", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore.Migrations;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
public partial class AddedValidUntilToApiKeys : Migration
|
|
||||||
{
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Up(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.AddColumn<DateTimeOffset>(
|
|
||||||
name: "ValidUntil",
|
|
||||||
schema: "core",
|
|
||||||
table: "ApiKeys",
|
|
||||||
type: "timestamp with time zone",
|
|
||||||
nullable: false,
|
|
||||||
defaultValue: new DateTimeOffset(new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified), new TimeSpan(0, 0, 0, 0, 0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Down(MigrationBuilder migrationBuilder)
|
|
||||||
{
|
|
||||||
migrationBuilder.DropColumn(
|
|
||||||
name: "ValidUntil",
|
|
||||||
schema: "core",
|
|
||||||
table: "ApiKeys");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,251 +0,0 @@
|
|||||||
// <auto-generated />
|
|
||||||
using System;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
|
||||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
|
||||||
|
|
||||||
#nullable disable
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Database.Migrations
|
|
||||||
{
|
|
||||||
[DbContext(typeof(DataContext))]
|
|
||||||
partial class DataContextModelSnapshot : ModelSnapshot
|
|
||||||
{
|
|
||||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
|
||||||
{
|
|
||||||
#pragma warning disable 612, 618
|
|
||||||
modelBuilder
|
|
||||||
.HasDefaultSchema("core")
|
|
||||||
.HasAnnotation("ProductVersion", "10.0.1")
|
|
||||||
.HasAnnotation("Relational:MaxIdentifierLength", 63);
|
|
||||||
|
|
||||||
NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder);
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.ApiKey", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(32)
|
|
||||||
.HasColumnType("character varying(32)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("ValidUntil")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("ApiKeys", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Description")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(300)
|
|
||||||
.HasColumnType("character varying(300)");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.PrimitiveCollection<string[]>("Permissions")
|
|
||||||
.IsRequired()
|
|
||||||
.HasColumnType("text[]");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Roles", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("RoleId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<int>("UserId")
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.HasIndex("RoleId");
|
|
||||||
|
|
||||||
b.HasIndex("UserId");
|
|
||||||
|
|
||||||
b.ToTable("RoleMembers", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.SettingsOption", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Key")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(256)
|
|
||||||
.HasColumnType("character varying(256)");
|
|
||||||
|
|
||||||
b.Property<string>("ValueJson")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(4096)
|
|
||||||
.HasColumnType("jsonb");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("SettingsOptions", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Theme", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<string>("Author")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("CssContent")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(20000)
|
|
||||||
.HasColumnType("character varying(20000)");
|
|
||||||
|
|
||||||
b.Property<bool>("IsEnabled")
|
|
||||||
.HasColumnType("boolean");
|
|
||||||
|
|
||||||
b.Property<string>("Name")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.Property<string>("Version")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(30)
|
|
||||||
.HasColumnType("character varying(30)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Themes", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Property<int>("Id")
|
|
||||||
.ValueGeneratedOnAdd()
|
|
||||||
.HasColumnType("integer");
|
|
||||||
|
|
||||||
NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property<int>("Id"));
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("CreatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Email")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(254)
|
|
||||||
.HasColumnType("character varying(254)");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("InvalidateTimestamp")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<DateTimeOffset>("UpdatedAt")
|
|
||||||
.HasColumnType("timestamp with time zone");
|
|
||||||
|
|
||||||
b.Property<string>("Username")
|
|
||||||
.IsRequired()
|
|
||||||
.HasMaxLength(50)
|
|
||||||
.HasColumnType("character varying(50)");
|
|
||||||
|
|
||||||
b.HasKey("Id");
|
|
||||||
|
|
||||||
b.ToTable("Users", "core");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.RoleMember", b =>
|
|
||||||
{
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.Role", "Role")
|
|
||||||
.WithMany("Members")
|
|
||||||
.HasForeignKey("RoleId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.HasOne("Moonlight.Api.Database.Entities.User", "User")
|
|
||||||
.WithMany("RoleMemberships")
|
|
||||||
.HasForeignKey("UserId")
|
|
||||||
.OnDelete(DeleteBehavior.Cascade)
|
|
||||||
.IsRequired();
|
|
||||||
|
|
||||||
b.Navigation("Role");
|
|
||||||
|
|
||||||
b.Navigation("User");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.Role", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("Members");
|
|
||||||
});
|
|
||||||
|
|
||||||
modelBuilder.Entity("Moonlight.Api.Database.Entities.User", b =>
|
|
||||||
{
|
|
||||||
b.Navigation("RoleMemberships");
|
|
||||||
});
|
|
||||||
#pragma warning restore 612, 618
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Logging.Abstractions;
|
|
||||||
using Microsoft.Extensions.Logging.Console;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Helpers;
|
|
||||||
|
|
||||||
public class AppConsoleFormatter : ConsoleFormatter
|
|
||||||
{
|
|
||||||
private const string TimestampColor = "\e[38;2;148;148;148m";
|
|
||||||
private const string CategoryColor = "\e[38;2;198;198;198m";
|
|
||||||
private const string MessageColor = "\e[38;2;255;255;255m";
|
|
||||||
private const string Bold = "\e[1m";
|
|
||||||
|
|
||||||
// Pre-computed ANSI color codes for each log level
|
|
||||||
private const string CriticalColor = "\e[38;2;255;0;0m";
|
|
||||||
private const string ErrorColor = "\e[38;2;255;0;0m";
|
|
||||||
private const string WarningColor = "\e[38;2;215;215;0m";
|
|
||||||
private const string InfoColor = "\e[38;2;135;215;255m";
|
|
||||||
private const string DebugColor = "\e[38;2;198;198;198m";
|
|
||||||
private const string TraceColor = "\e[38;2;68;68;68m";
|
|
||||||
|
|
||||||
public AppConsoleFormatter() : base(nameof(AppConsoleFormatter))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Write<TState>(
|
|
||||||
in LogEntry<TState> logEntry,
|
|
||||||
IExternalScopeProvider? scopeProvider,
|
|
||||||
TextWriter textWriter)
|
|
||||||
{
|
|
||||||
var message = logEntry.Formatter(logEntry.State, logEntry.Exception);
|
|
||||||
|
|
||||||
// Timestamp
|
|
||||||
textWriter.Write(TimestampColor);
|
|
||||||
textWriter.Write(DateTime.Now.ToString("dd.MM.yy HH:mm:ss"));
|
|
||||||
textWriter.Write(' ');
|
|
||||||
|
|
||||||
// Log level with color and bold
|
|
||||||
var (levelText, levelColor) = GetLevelInfo(logEntry.LogLevel);
|
|
||||||
textWriter.Write(levelColor);
|
|
||||||
textWriter.Write(Bold);
|
|
||||||
textWriter.Write(levelText);
|
|
||||||
textWriter.Write(' ');
|
|
||||||
|
|
||||||
// Category
|
|
||||||
textWriter.Write(CategoryColor);
|
|
||||||
textWriter.Write(logEntry.Category);
|
|
||||||
|
|
||||||
// Message
|
|
||||||
textWriter.Write(MessageColor);
|
|
||||||
textWriter.Write(": ");
|
|
||||||
textWriter.Write(message);
|
|
||||||
|
|
||||||
// Exception
|
|
||||||
if (logEntry.Exception != null)
|
|
||||||
{
|
|
||||||
textWriter.Write(MessageColor);
|
|
||||||
textWriter.WriteLine(logEntry.Exception.ToString());
|
|
||||||
}
|
|
||||||
else
|
|
||||||
textWriter.WriteLine();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (string text, string color) GetLevelInfo(LogLevel logLevel)
|
|
||||||
{
|
|
||||||
return logLevel switch
|
|
||||||
{
|
|
||||||
LogLevel.Critical => ("CRIT", CriticalColor),
|
|
||||||
LogLevel.Error => ("ERRO", ErrorColor),
|
|
||||||
LogLevel.Warning => ("WARN", WarningColor),
|
|
||||||
LogLevel.Information => ("INFO", InfoColor),
|
|
||||||
LogLevel.Debug => ("DEBG", DebugColor),
|
|
||||||
LogLevel.Trace => ("TRCE", TraceColor),
|
|
||||||
_ => ("NONE", "")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,98 +0,0 @@
|
|||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Helpers;
|
|
||||||
|
|
||||||
public class OsHelper
|
|
||||||
{
|
|
||||||
public static string GetName()
|
|
||||||
{
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
|
||||||
return GetWindowsVersion();
|
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
|
||||||
return GetLinuxVersion();
|
|
||||||
|
|
||||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
|
||||||
return "Goofy OS";
|
|
||||||
|
|
||||||
return "Unknown OS";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetWindowsVersion()
|
|
||||||
{
|
|
||||||
var version = Environment.OSVersion.Version;
|
|
||||||
|
|
||||||
// Windows 11 is version 10.0 build 22000+
|
|
||||||
if (version.Major == 10 && version.Build >= 22000)
|
|
||||||
return $"Windows 11 ({version.Build})";
|
|
||||||
|
|
||||||
if (version.Major == 10)
|
|
||||||
return $"Windows 10 ({version.Build})";
|
|
||||||
|
|
||||||
if (version.Major == 6 && version.Minor == 3)
|
|
||||||
return "Windows 8.1";
|
|
||||||
|
|
||||||
if (version.Major == 6 && version.Minor == 2)
|
|
||||||
return "Windows 8";
|
|
||||||
|
|
||||||
if (version.Major == 6 && version.Minor == 1)
|
|
||||||
return "Windows 7";
|
|
||||||
|
|
||||||
return $"Windows {version.Major}.{version.Minor}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetLinuxVersion()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// Read /etc/os-release, should work everywhere
|
|
||||||
if (File.Exists("/etc/os-release"))
|
|
||||||
{
|
|
||||||
var lines = File.ReadAllLines("/etc/os-release");
|
|
||||||
string? name = null;
|
|
||||||
string? version = null;
|
|
||||||
|
|
||||||
foreach (var line in lines)
|
|
||||||
{
|
|
||||||
if (line.StartsWith("NAME="))
|
|
||||||
name = line.Substring(5).Trim('"');
|
|
||||||
else if (line.StartsWith("VERSION_ID="))
|
|
||||||
version = line.Substring(11).Trim('"');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
return string.IsNullOrEmpty(version) ? name : $"{name} {version}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//If for some weird reason it still uses lsb release
|
|
||||||
if (File.Exists("/etc/lsb-release"))
|
|
||||||
{
|
|
||||||
var lines = File.ReadAllLines("/etc/lsb-release");
|
|
||||||
string? name = null;
|
|
||||||
string? version = null;
|
|
||||||
|
|
||||||
foreach (var line in lines)
|
|
||||||
{
|
|
||||||
if (line.StartsWith("DISTRIB_ID="))
|
|
||||||
name = line.Substring(11);
|
|
||||||
else if (line.StartsWith("DISTRIB_RELEASE="))
|
|
||||||
version = line.Substring(16);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(name))
|
|
||||||
{
|
|
||||||
return string.IsNullOrEmpty(version) ? name : $"{name} {version}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// Ignore
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback
|
|
||||||
return $"Linux {Environment.OSVersion.Version}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,143 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Caching.Hybrid;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Implementations.ApiKeyScheme;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Requests;
|
|
||||||
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
|
||||||
using Moonlight.Shared.Http.Responses;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/apiKeys")]
|
|
||||||
public class ApiKeyController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<ApiKey> KeyRepository;
|
|
||||||
private readonly HybridCache HybridCache;
|
|
||||||
|
|
||||||
public ApiKeyController(DatabaseRepository<ApiKey> keyRepository, HybridCache hybridCache)
|
|
||||||
{
|
|
||||||
KeyRepository = keyRepository;
|
|
||||||
HybridCache = hybridCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = Permissions.ApiKeys.View)]
|
|
||||||
public async Task<ActionResult<PagedData<ApiKeyDto>>> GetAsync(
|
|
||||||
[FromQuery] int startIndex,
|
|
||||||
[FromQuery] int length,
|
|
||||||
[FromQuery] FilterOptions? filterOptions
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Validation
|
|
||||||
if (startIndex < 0)
|
|
||||||
return Problem("Invalid start index specified", statusCode: 400);
|
|
||||||
|
|
||||||
if (length is < 1 or > 100)
|
|
||||||
return Problem("Invalid length specified");
|
|
||||||
|
|
||||||
// Query building
|
|
||||||
var query = KeyRepository.Query();
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (filterOptions != null)
|
|
||||||
{
|
|
||||||
foreach (var filterOption in filterOptions.Filters)
|
|
||||||
{
|
|
||||||
query = filterOption.Key switch
|
|
||||||
{
|
|
||||||
nameof(ApiKey.Name) =>
|
|
||||||
query.Where(k => EF.Functions.ILike(k.Name, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
nameof(ApiKey.Description) =>
|
|
||||||
query.Where(k => EF.Functions.ILike(k.Description, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
_ => query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
var data = await query
|
|
||||||
.OrderBy(k => k.Id)
|
|
||||||
.ProjectToDto()
|
|
||||||
.Skip(startIndex)
|
|
||||||
.Take(length)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var total = await query.CountAsync();
|
|
||||||
|
|
||||||
return new PagedData<ApiKeyDto>(data, total);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.ApiKeys.View)]
|
|
||||||
public async Task<ActionResult<ApiKeyDto>> GetAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var key = await KeyRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(k => k.Id == id);
|
|
||||||
|
|
||||||
if (key == null)
|
|
||||||
return Problem("No API key with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
return ApiKeyMapper.ToDto(key);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = Permissions.ApiKeys.Create)]
|
|
||||||
public async Task<ActionResult<ApiKeyDto>> CreateAsync([FromBody] CreateApiKeyDto request)
|
|
||||||
{
|
|
||||||
var apiKey = ApiKeyMapper.ToEntity(request);
|
|
||||||
|
|
||||||
apiKey.Key = Guid.NewGuid().ToString("N").Substring(0, 32);
|
|
||||||
|
|
||||||
var finalKey = await KeyRepository.AddAsync(apiKey);
|
|
||||||
|
|
||||||
return ApiKeyMapper.ToDto(finalKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.ApiKeys.Edit)]
|
|
||||||
public async Task<ActionResult<ApiKeyDto>> UpdateAsync([FromRoute] int id, [FromBody] UpdateApiKeyDto request)
|
|
||||||
{
|
|
||||||
var apiKey = await KeyRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(k => k.Id == id);
|
|
||||||
|
|
||||||
if (apiKey == null)
|
|
||||||
return Problem("No API key with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
ApiKeyMapper.Merge(apiKey, request);
|
|
||||||
await KeyRepository.UpdateAsync(apiKey);
|
|
||||||
|
|
||||||
await HybridCache.RemoveAsync(string.Format(ApiKeySchemeHandler.CacheKeyFormat, apiKey.Key));
|
|
||||||
|
|
||||||
return ApiKeyMapper.ToDto(apiKey);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.ApiKeys.Delete)]
|
|
||||||
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var apiKey = await KeyRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(k => k.Id == id);
|
|
||||||
|
|
||||||
if (apiKey == null)
|
|
||||||
return Problem("No API key with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
await KeyRepository.RemoveAsync(apiKey);
|
|
||||||
|
|
||||||
await HybridCache.RemoveAsync(string.Format(ApiKeySchemeHandler.CacheKeyFormat, apiKey.Key));
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Http;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Moonlight.Api.Configuration;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Requests.Admin.ContainerHelper;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/ch")]
|
|
||||||
[Authorize(Policy = Permissions.System.Instance)]
|
|
||||||
public class ContainerHelperController : Controller
|
|
||||||
{
|
|
||||||
private readonly ContainerHelperService ContainerHelperService;
|
|
||||||
private readonly IOptions<ContainerHelperOptions> Options;
|
|
||||||
|
|
||||||
public ContainerHelperController(ContainerHelperService containerHelperService, IOptions<ContainerHelperOptions> options)
|
|
||||||
{
|
|
||||||
ContainerHelperService = containerHelperService;
|
|
||||||
Options = options;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("status")]
|
|
||||||
public async Task<ActionResult<ContainerHelperStatusDto>> GetStatusAsync()
|
|
||||||
{
|
|
||||||
if (!Options.Value.IsEnabled)
|
|
||||||
return new ContainerHelperStatusDto(false, false);
|
|
||||||
|
|
||||||
var status = await ContainerHelperService.CheckConnectionAsync();
|
|
||||||
|
|
||||||
return new ContainerHelperStatusDto(true, status);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("rebuild")]
|
|
||||||
public Task<IResult> RebuildAsync([FromBody] RequestRebuildDto request)
|
|
||||||
{
|
|
||||||
var result = ContainerHelperService.RebuildAsync(request.NoBuildCache);
|
|
||||||
var mappedResult = result.Select(ContainerHelperMapper.ToDto);
|
|
||||||
|
|
||||||
return Task.FromResult<IResult>(
|
|
||||||
TypedResults.ServerSentEvents(mappedResult)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("version")]
|
|
||||||
public async Task<ActionResult> SetVersionAsync([FromBody] SetVersionDto request)
|
|
||||||
{
|
|
||||||
await ContainerHelperService.SetVersionAsync(request.Version);
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Authorize(Policy = Permissions.System.Diagnose)]
|
|
||||||
[Route("api/admin/system/diagnose")]
|
|
||||||
public class DiagnoseController : Controller
|
|
||||||
{
|
|
||||||
private readonly DiagnoseService DiagnoseService;
|
|
||||||
|
|
||||||
public DiagnoseController(DiagnoseService diagnoseService)
|
|
||||||
{
|
|
||||||
DiagnoseService = diagnoseService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<ActionResult<DiagnoseResultDto[]>> GetAsync()
|
|
||||||
{
|
|
||||||
var results = await DiagnoseService.DiagnoseAsync();
|
|
||||||
|
|
||||||
return results
|
|
||||||
.OrderBy(x => x.Level)
|
|
||||||
.ToDto()
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Responses;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Users;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Authorize(Policy = Permissions.Roles.Members)]
|
|
||||||
[Route("api/admin/roles/{roleId:int}/members")]
|
|
||||||
public class RoleMembersController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<User> UsersRepository;
|
|
||||||
private readonly DatabaseRepository<Role> RolesRepository;
|
|
||||||
private readonly DatabaseRepository<RoleMember> RoleMembersRepository;
|
|
||||||
|
|
||||||
public RoleMembersController(
|
|
||||||
DatabaseRepository<User> usersRepository,
|
|
||||||
DatabaseRepository<Role> rolesRepository,
|
|
||||||
DatabaseRepository<RoleMember> roleMembersRepository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
UsersRepository = usersRepository;
|
|
||||||
RolesRepository = rolesRepository;
|
|
||||||
RoleMembersRepository = roleMembersRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<ActionResult<PagedData<UserDto>>> GetAsync(
|
|
||||||
[FromRoute] int roleId,
|
|
||||||
[FromQuery] int startIndex, [FromQuery] int length,
|
|
||||||
[FromQuery] string? searchTerm
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Validation
|
|
||||||
if (startIndex < 0)
|
|
||||||
return Problem("Invalid start index specified", statusCode: 400);
|
|
||||||
|
|
||||||
if (length is < 1 or > 100)
|
|
||||||
return Problem("Invalid length specified");
|
|
||||||
|
|
||||||
// Query building
|
|
||||||
|
|
||||||
var query = RoleMembersRepository
|
|
||||||
.Query()
|
|
||||||
.Where(x => x.Role.Id == roleId)
|
|
||||||
.Select(x => x.User);
|
|
||||||
|
|
||||||
// Filtering
|
|
||||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
|
||||||
{
|
|
||||||
query = query.Where(x =>
|
|
||||||
EF.Functions.ILike(x.Username, $"%{searchTerm}%") ||
|
|
||||||
EF.Functions.ILike(x.Email, $"%{searchTerm}%")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
var items = query
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.Skip(startIndex)
|
|
||||||
.Take(length)
|
|
||||||
.ProjectToDto()
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var totalCount = await query.CountAsync();
|
|
||||||
|
|
||||||
return new PagedData<UserDto>(items, totalCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("available")]
|
|
||||||
public async Task<ActionResult<PagedData<UserDto>>> GetAvailableAsync(
|
|
||||||
[FromRoute] int roleId,
|
|
||||||
[FromQuery] int startIndex, [FromQuery] int length,
|
|
||||||
[FromQuery] string? searchTerm
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Validation
|
|
||||||
if (startIndex < 0)
|
|
||||||
return Problem("Invalid start index specified", statusCode: 400);
|
|
||||||
|
|
||||||
if (length is < 1 or > 100)
|
|
||||||
return Problem("Invalid length specified");
|
|
||||||
|
|
||||||
// Query building
|
|
||||||
|
|
||||||
var query = UsersRepository
|
|
||||||
.Query()
|
|
||||||
.Where(x => x.RoleMemberships.All(y => y.Role.Id != roleId));
|
|
||||||
|
|
||||||
// Filtering
|
|
||||||
if (!string.IsNullOrWhiteSpace(searchTerm))
|
|
||||||
{
|
|
||||||
query = query.Where(x =>
|
|
||||||
EF.Functions.ILike(x.Username, $"%{searchTerm}%") ||
|
|
||||||
EF.Functions.ILike(x.Email, $"%{searchTerm}%")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
var items = query
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.Skip(startIndex)
|
|
||||||
.Take(length)
|
|
||||||
.ProjectToDto()
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var totalCount = await query.CountAsync();
|
|
||||||
|
|
||||||
return new PagedData<UserDto>(items, totalCount);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPut("{userId:int}")]
|
|
||||||
public async Task<ActionResult> AddMemberAsync([FromRoute] int roleId, [FromRoute] int userId)
|
|
||||||
{
|
|
||||||
// Check and load role
|
|
||||||
var role = await RolesRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == roleId);
|
|
||||||
|
|
||||||
if (role == null)
|
|
||||||
return Problem("Role not found", statusCode: 404);
|
|
||||||
|
|
||||||
// Check and load user
|
|
||||||
var user = await UsersRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == userId);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
return Problem("User not found", statusCode: 404);
|
|
||||||
|
|
||||||
// Check if a role member already exists with these details
|
|
||||||
var isUserInRole = await RoleMembersRepository
|
|
||||||
.Query()
|
|
||||||
.AnyAsync(x => x.Role.Id == roleId && x.User.Id == userId);
|
|
||||||
|
|
||||||
if (isUserInRole)
|
|
||||||
return Problem("User is already a member of this role", statusCode: 400);
|
|
||||||
|
|
||||||
var roleMember = new RoleMember
|
|
||||||
{
|
|
||||||
Role = role,
|
|
||||||
User = user
|
|
||||||
};
|
|
||||||
|
|
||||||
await RoleMembersRepository.AddAsync(roleMember);
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{userId:int}")]
|
|
||||||
public async Task<ActionResult> RemoveMemberAsync([FromRoute] int roleId, [FromRoute] int userId)
|
|
||||||
{
|
|
||||||
var roleMember = await RoleMembersRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.User.Id == userId && x.Role.Id == roleId);
|
|
||||||
|
|
||||||
if (roleMember == null)
|
|
||||||
return Problem("User is not a member of this role, the role does not exist or the user does not exist",
|
|
||||||
statusCode: 404);
|
|
||||||
|
|
||||||
await RoleMembersRepository.RemoveAsync(roleMember);
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Requests;
|
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Roles;
|
|
||||||
using Moonlight.Shared.Http.Responses;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/roles")]
|
|
||||||
public class RolesController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Role> RoleRepository;
|
|
||||||
|
|
||||||
public RolesController(DatabaseRepository<Role> roleRepository)
|
|
||||||
{
|
|
||||||
RoleRepository = roleRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = Permissions.Roles.View)]
|
|
||||||
public async Task<ActionResult<PagedData<RoleDto>>> GetAsync(
|
|
||||||
[FromQuery] int startIndex,
|
|
||||||
[FromQuery] int length,
|
|
||||||
[FromQuery] FilterOptions? filterOptions
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Validation
|
|
||||||
if (startIndex < 0)
|
|
||||||
return Problem("Invalid start index specified", statusCode: 400);
|
|
||||||
|
|
||||||
if (length is < 1 or > 100)
|
|
||||||
return Problem("Invalid length specified");
|
|
||||||
|
|
||||||
// Query building
|
|
||||||
|
|
||||||
var query = RoleRepository
|
|
||||||
.Query();
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (filterOptions != null)
|
|
||||||
{
|
|
||||||
foreach (var filterOption in filterOptions.Filters)
|
|
||||||
{
|
|
||||||
query = filterOption.Key switch
|
|
||||||
{
|
|
||||||
nameof(Role.Name) =>
|
|
||||||
query.Where(role => EF.Functions.ILike(role.Name, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
_ => query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
var data = await query
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ProjectToDto()
|
|
||||||
.Skip(startIndex)
|
|
||||||
.Take(length)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var total = await query.CountAsync();
|
|
||||||
|
|
||||||
return new PagedData<RoleDto>(data, total);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Roles.View)]
|
|
||||||
public async Task<ActionResult<RoleDto>> GetAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var role = await RoleRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (role == null)
|
|
||||||
return Problem("No role with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
return RoleMapper.ToDto(role);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = Permissions.Roles.Create)]
|
|
||||||
public async Task<ActionResult<RoleDto>> CreateAsync([FromBody] CreateRoleDto request)
|
|
||||||
{
|
|
||||||
var role = RoleMapper.ToEntity(request);
|
|
||||||
|
|
||||||
var finalRole = await RoleRepository.AddAsync(role);
|
|
||||||
|
|
||||||
return RoleMapper.ToDto(finalRole);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Roles.Edit)]
|
|
||||||
public async Task<ActionResult<RoleDto>> UpdateAsync([FromRoute] int id, [FromBody] UpdateRoleDto request)
|
|
||||||
{
|
|
||||||
var role = await RoleRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (role == null)
|
|
||||||
return Problem("No role with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
RoleMapper.Merge(role, request);
|
|
||||||
|
|
||||||
await RoleRepository.UpdateAsync(role);
|
|
||||||
|
|
||||||
return RoleMapper.ToDto(role);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Roles.Delete)]
|
|
||||||
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var role = await RoleRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (role == null)
|
|
||||||
return Problem("No role with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
await RoleRepository.RemoveAsync(role);
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,129 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Requests.Seup;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/setup")]
|
|
||||||
public class SetupController : Controller
|
|
||||||
{
|
|
||||||
private readonly SettingsService SettingsService;
|
|
||||||
private readonly DatabaseRepository<User> UsersRepository;
|
|
||||||
private readonly DatabaseRepository<Role> RolesRepository;
|
|
||||||
|
|
||||||
private const string StateSettingsKey = "Moonlight.Api.Setup.State";
|
|
||||||
|
|
||||||
public SetupController(
|
|
||||||
SettingsService settingsService,
|
|
||||||
DatabaseRepository<User> usersRepository,
|
|
||||||
DatabaseRepository<Role> rolesRepository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
SettingsService = settingsService;
|
|
||||||
UsersRepository = usersRepository;
|
|
||||||
RolesRepository = rolesRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[AllowAnonymous]
|
|
||||||
public async Task<ActionResult> GetSetupAsync()
|
|
||||||
{
|
|
||||||
var hasBeenSetup = await SettingsService.GetValueAsync<bool>(StateSettingsKey);
|
|
||||||
|
|
||||||
if (hasBeenSetup)
|
|
||||||
return Problem("This instance is already configured", statusCode: 405);
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[AllowAnonymous]
|
|
||||||
public async Task<ActionResult> ApplySetupAsync([FromBody] ApplySetupDto dto)
|
|
||||||
{
|
|
||||||
var adminRole = await RolesRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Name == "Administrators");
|
|
||||||
|
|
||||||
if (adminRole == null)
|
|
||||||
{
|
|
||||||
adminRole = await RolesRepository.AddAsync(new Role()
|
|
||||||
{
|
|
||||||
Name = "Administrators",
|
|
||||||
Description = "Automatically generated group for full administrator permissions",
|
|
||||||
Permissions = [
|
|
||||||
Permissions.ApiKeys.View,
|
|
||||||
Permissions.ApiKeys.Create,
|
|
||||||
Permissions.ApiKeys.Edit,
|
|
||||||
Permissions.ApiKeys.Delete,
|
|
||||||
|
|
||||||
Permissions.Roles.View,
|
|
||||||
Permissions.Roles.Create,
|
|
||||||
Permissions.Roles.Edit,
|
|
||||||
Permissions.Roles.Delete,
|
|
||||||
Permissions.Roles.Members,
|
|
||||||
|
|
||||||
Permissions.Users.View,
|
|
||||||
Permissions.Users.Create,
|
|
||||||
Permissions.Users.Edit,
|
|
||||||
Permissions.Users.Delete,
|
|
||||||
Permissions.Users.Logout,
|
|
||||||
|
|
||||||
Permissions.Themes.View,
|
|
||||||
Permissions.Themes.Create,
|
|
||||||
Permissions.Themes.Edit,
|
|
||||||
Permissions.Themes.Delete,
|
|
||||||
|
|
||||||
Permissions.System.Info,
|
|
||||||
Permissions.System.Diagnose,
|
|
||||||
Permissions.System.Versions,
|
|
||||||
Permissions.System.Instance,
|
|
||||||
]
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var user = await UsersRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(u => u.Email == dto.AdminEmail);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
{
|
|
||||||
await UsersRepository.AddAsync(new User()
|
|
||||||
{
|
|
||||||
Email = dto.AdminEmail,
|
|
||||||
Username = dto.AdminUsername,
|
|
||||||
RoleMemberships = [
|
|
||||||
new RoleMember()
|
|
||||||
{
|
|
||||||
Role = adminRole,
|
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
|
||||||
UpdatedAt = DateTimeOffset.UtcNow
|
|
||||||
}
|
|
||||||
],
|
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
|
||||||
UpdatedAt = DateTimeOffset.UtcNow
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user.RoleMemberships.Add(new RoleMember()
|
|
||||||
{
|
|
||||||
Role = adminRole,
|
|
||||||
CreatedAt = DateTimeOffset.UtcNow,
|
|
||||||
UpdatedAt = DateTimeOffset.UtcNow
|
|
||||||
});
|
|
||||||
|
|
||||||
await UsersRepository.UpdateAsync(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
await SettingsService.SetValueAsync(StateSettingsKey, true);
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/system")]
|
|
||||||
public class SystemController : Controller
|
|
||||||
{
|
|
||||||
private readonly ApplicationService ApplicationService;
|
|
||||||
|
|
||||||
public SystemController(ApplicationService applicationService)
|
|
||||||
{
|
|
||||||
ApplicationService = applicationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("info")]
|
|
||||||
[Authorize(Policy = Permissions.System.Info)]
|
|
||||||
public async Task<ActionResult<SystemInfoDto>> GetInfoAsync()
|
|
||||||
{
|
|
||||||
var cpuUsage = await ApplicationService.GetCpuUsageAsync();
|
|
||||||
var memoryUsage = await ApplicationService.GetMemoryUsageAsync();
|
|
||||||
|
|
||||||
return new SystemInfoDto(
|
|
||||||
cpuUsage,
|
|
||||||
memoryUsage,
|
|
||||||
ApplicationService.OperatingSystem,
|
|
||||||
DateTimeOffset.UtcNow - ApplicationService.StartedAt,
|
|
||||||
ApplicationService.VersionName,
|
|
||||||
ApplicationService.IsUpToDate
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,145 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Requests;
|
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Themes;
|
|
||||||
using Moonlight.Shared.Http.Responses;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Themes;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin.Themes;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/themes")]
|
|
||||||
public class ThemesController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Theme> ThemeRepository;
|
|
||||||
private readonly FrontendService FrontendService;
|
|
||||||
|
|
||||||
public ThemesController(DatabaseRepository<Theme> themeRepository, FrontendService frontendService)
|
|
||||||
{
|
|
||||||
ThemeRepository = themeRepository;
|
|
||||||
FrontendService = frontendService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = Permissions.Themes.View)]
|
|
||||||
public async Task<ActionResult<PagedData<ThemeDto>>> GetAsync(
|
|
||||||
[FromQuery] int startIndex,
|
|
||||||
[FromQuery] int length,
|
|
||||||
[FromQuery] FilterOptions? filterOptions
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Validation
|
|
||||||
if (startIndex < 0)
|
|
||||||
return Problem("Invalid start index specified", statusCode: 400);
|
|
||||||
|
|
||||||
if (length is < 1 or > 100)
|
|
||||||
return Problem("Invalid length specified");
|
|
||||||
|
|
||||||
// Query building
|
|
||||||
|
|
||||||
var query = ThemeRepository
|
|
||||||
.Query();
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (filterOptions != null)
|
|
||||||
{
|
|
||||||
foreach (var filterOption in filterOptions.Filters)
|
|
||||||
{
|
|
||||||
query = filterOption.Key switch
|
|
||||||
{
|
|
||||||
nameof(Theme.Name) =>
|
|
||||||
query.Where(user => EF.Functions.ILike(user.Name, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
nameof(Theme.Version) =>
|
|
||||||
query.Where(user => EF.Functions.ILike(user.Version, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
nameof(Theme.Author) =>
|
|
||||||
query.Where(user => EF.Functions.ILike(user.Author, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
_ => query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
var data = await query
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ProjectToDto()
|
|
||||||
.Skip(startIndex)
|
|
||||||
.Take(length)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var total = await query.CountAsync();
|
|
||||||
|
|
||||||
return new PagedData<ThemeDto>(data, total);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Themes.View)]
|
|
||||||
public async Task<ActionResult<ThemeDto>> GetAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var item = await ThemeRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (item == null)
|
|
||||||
return Problem("No theme with this id", statusCode: 404);
|
|
||||||
|
|
||||||
return ThemeMapper.ToDto(item);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = Permissions.Themes.Create)]
|
|
||||||
public async Task<ActionResult<ThemeDto>> CreateAsync([FromBody] CreateThemeDto request)
|
|
||||||
{
|
|
||||||
var theme = ThemeMapper.ToEntity(request);
|
|
||||||
|
|
||||||
var finalTheme = await ThemeRepository.AddAsync(theme);
|
|
||||||
await FrontendService.ResetCacheAsync();
|
|
||||||
|
|
||||||
return ThemeMapper.ToDto(finalTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Themes.Edit)]
|
|
||||||
public async Task<ActionResult<ThemeDto>> UpdateAsync([FromRoute] int id, [FromBody] UpdateThemeDto request)
|
|
||||||
{
|
|
||||||
var theme = await ThemeRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (theme == null)
|
|
||||||
return Problem("No theme with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
ThemeMapper.Merge(theme, request);
|
|
||||||
await ThemeRepository.UpdateAsync(theme);
|
|
||||||
|
|
||||||
await FrontendService.ResetCacheAsync();
|
|
||||||
|
|
||||||
return ThemeMapper.ToDto(theme);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Themes.Delete)]
|
|
||||||
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var theme = await ThemeRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (theme == null)
|
|
||||||
return Problem("No theme with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
await ThemeRepository.RemoveAsync(theme);
|
|
||||||
|
|
||||||
await FrontendService.ResetCacheAsync();
|
|
||||||
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Api.Models;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Themes;
|
|
||||||
using VYaml.Serialization;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin.Themes;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/themes")]
|
|
||||||
[Authorize(Policy = Permissions.Themes.View)]
|
|
||||||
public class TransferController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<Theme> ThemeRepository;
|
|
||||||
|
|
||||||
public TransferController(DatabaseRepository<Theme> themeRepository)
|
|
||||||
{
|
|
||||||
ThemeRepository = themeRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}/export")]
|
|
||||||
public async Task<ActionResult> ExportAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var theme = await ThemeRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (theme == null)
|
|
||||||
return Problem("No theme with that id found", statusCode: 404);
|
|
||||||
|
|
||||||
var yml = YamlSerializer.Serialize(new ThemeTransferModel()
|
|
||||||
{
|
|
||||||
Name = theme.Name,
|
|
||||||
Author = theme.Author,
|
|
||||||
CssContent = theme.CssContent,
|
|
||||||
Version = theme.Version
|
|
||||||
});
|
|
||||||
|
|
||||||
return File(yml.ToArray(), "text/yaml", $"{theme.Name}.yml");
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost("import")]
|
|
||||||
public async Task<ActionResult<ThemeDto>> ImportAsync()
|
|
||||||
{
|
|
||||||
var themeToImport = await YamlSerializer.DeserializeAsync<ThemeTransferModel>(Request.Body);
|
|
||||||
|
|
||||||
var existingTheme = await ThemeRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Name == themeToImport.Name && x.Author == themeToImport.Author);
|
|
||||||
|
|
||||||
if (existingTheme == null)
|
|
||||||
{
|
|
||||||
var finalTheme = await ThemeRepository.AddAsync(new Theme()
|
|
||||||
{
|
|
||||||
Name = themeToImport.Name,
|
|
||||||
Author = themeToImport.Author,
|
|
||||||
CssContent = themeToImport.CssContent,
|
|
||||||
Version = themeToImport.Version
|
|
||||||
});
|
|
||||||
|
|
||||||
return ThemeMapper.ToDto(finalTheme);
|
|
||||||
}
|
|
||||||
|
|
||||||
existingTheme.CssContent = themeToImport.CssContent;
|
|
||||||
existingTheme.Version = themeToImport.Version;
|
|
||||||
|
|
||||||
await ThemeRepository.UpdateAsync(existingTheme);
|
|
||||||
|
|
||||||
return ThemeMapper.ToDto(existingTheme);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using System.Collections.Frozen;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin.Users;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/users")]
|
|
||||||
[Authorize(Policy = Permissions.Users.Delete)]
|
|
||||||
public class UserDeletionController : Controller
|
|
||||||
{
|
|
||||||
private readonly UserDeletionService UserDeletionService;
|
|
||||||
private readonly DatabaseRepository<User> Repository;
|
|
||||||
|
|
||||||
public UserDeletionController(UserDeletionService userDeletionService, DatabaseRepository<User> repository)
|
|
||||||
{
|
|
||||||
UserDeletionService = userDeletionService;
|
|
||||||
Repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpDelete("{id:int}")]
|
|
||||||
public async Task<ActionResult> DeleteAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var userExists = await Repository
|
|
||||||
.Query()
|
|
||||||
.AnyAsync(user => user.Id == id);
|
|
||||||
|
|
||||||
if (!userExists)
|
|
||||||
return Problem("No user with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
var validationResult = await UserDeletionService.ValidateAsync(id);
|
|
||||||
|
|
||||||
if (!validationResult.IsValid)
|
|
||||||
{
|
|
||||||
return ValidationProblem(
|
|
||||||
new ValidationProblemDetails(
|
|
||||||
new Dictionary<string, string[]>()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
string.Empty,
|
|
||||||
validationResult.ErrorMessages.ToArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
await UserDeletionService.DeleteAsync(id);
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,41 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin.Users;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/users/{id:int}/logout")]
|
|
||||||
[Authorize(Policy = Permissions.Users.Logout)]
|
|
||||||
public class UserLogoutController : Controller
|
|
||||||
{
|
|
||||||
private readonly UserLogoutService LogoutService;
|
|
||||||
private readonly DatabaseRepository<User> Repository;
|
|
||||||
|
|
||||||
public UserLogoutController(
|
|
||||||
UserLogoutService logoutService,
|
|
||||||
DatabaseRepository<User> repository
|
|
||||||
)
|
|
||||||
{
|
|
||||||
LogoutService = logoutService;
|
|
||||||
Repository = repository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
public async Task<ActionResult> LogoutAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var userExists = await Repository
|
|
||||||
.Query()
|
|
||||||
.AnyAsync(user => user.Id == id);
|
|
||||||
|
|
||||||
if (!userExists)
|
|
||||||
return Problem("No user with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
await LogoutService.LogoutAsync(id);
|
|
||||||
return NoContent();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,120 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Requests;
|
|
||||||
using Moonlight.Shared.Http.Requests.Admin.Users;
|
|
||||||
using Moonlight.Shared.Http.Responses;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Users;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin.Users;
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/users")]
|
|
||||||
public class UsersController : Controller
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<User> UserRepository;
|
|
||||||
|
|
||||||
public UsersController(DatabaseRepository<User> userRepository)
|
|
||||||
{
|
|
||||||
UserRepository = userRepository;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
[Authorize(Policy = Permissions.Users.View)]
|
|
||||||
public async Task<ActionResult<PagedData<UserDto>>> GetAsync(
|
|
||||||
[FromQuery] int startIndex,
|
|
||||||
[FromQuery] int length,
|
|
||||||
[FromQuery] FilterOptions? filterOptions
|
|
||||||
)
|
|
||||||
{
|
|
||||||
// Validation
|
|
||||||
if (startIndex < 0)
|
|
||||||
return Problem("Invalid start index specified", statusCode: 400);
|
|
||||||
|
|
||||||
if (length is < 1 or > 100)
|
|
||||||
return Problem("Invalid length specified");
|
|
||||||
|
|
||||||
// Query building
|
|
||||||
|
|
||||||
var query = UserRepository
|
|
||||||
.Query();
|
|
||||||
|
|
||||||
// Filters
|
|
||||||
if (filterOptions != null)
|
|
||||||
{
|
|
||||||
foreach (var filterOption in filterOptions.Filters)
|
|
||||||
{
|
|
||||||
query = filterOption.Key switch
|
|
||||||
{
|
|
||||||
nameof(Database.Entities.User.Email) =>
|
|
||||||
query.Where(user => EF.Functions.ILike(user.Email, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
nameof(Database.Entities.User.Username) =>
|
|
||||||
query.Where(user => EF.Functions.ILike(user.Username, $"%{filterOption.Value}%")),
|
|
||||||
|
|
||||||
_ => query
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pagination
|
|
||||||
var data = await query
|
|
||||||
.OrderBy(x => x.Id)
|
|
||||||
.ProjectToDto()
|
|
||||||
.Skip(startIndex)
|
|
||||||
.Take(length)
|
|
||||||
.ToArrayAsync();
|
|
||||||
|
|
||||||
var total = await query.CountAsync();
|
|
||||||
|
|
||||||
return new PagedData<UserDto>(data, total);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Users.View)]
|
|
||||||
public async Task<ActionResult<UserDto>> GetAsync([FromRoute] int id)
|
|
||||||
{
|
|
||||||
var user = await UserRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
return Problem("No user with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
return UserMapper.ToDto(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPost]
|
|
||||||
[Authorize(Policy = Permissions.Users.Create)]
|
|
||||||
public async Task<ActionResult<UserDto>> CreateAsync([FromBody] CreateUserDto request)
|
|
||||||
{
|
|
||||||
var user = UserMapper.ToEntity(request);
|
|
||||||
user.InvalidateTimestamp = DateTimeOffset.UtcNow.AddMinutes(-1);
|
|
||||||
|
|
||||||
var finalUser = await UserRepository.AddAsync(user);
|
|
||||||
|
|
||||||
return UserMapper.ToDto(finalUser);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpPatch("{id:int}")]
|
|
||||||
[Authorize(Policy = Permissions.Users.Edit)]
|
|
||||||
public async Task<ActionResult<UserDto>> UpdateAsync([FromRoute] int id, [FromBody] UpdateUserDto request)
|
|
||||||
{
|
|
||||||
var user = await UserRepository
|
|
||||||
.Query()
|
|
||||||
.FirstOrDefaultAsync(x => x.Id == id);
|
|
||||||
|
|
||||||
if (user == null)
|
|
||||||
return Problem("No user with this id found", statusCode: 404);
|
|
||||||
|
|
||||||
UserMapper.Merge(user, request);
|
|
||||||
await UserRepository.UpdateAsync(user);
|
|
||||||
|
|
||||||
return UserMapper.ToDto(user);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,46 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers.Admin;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/admin/versions")]
|
|
||||||
[Authorize(Policy = Permissions.System.Versions)]
|
|
||||||
public class VersionsController : Controller
|
|
||||||
{
|
|
||||||
private readonly VersionService VersionService;
|
|
||||||
|
|
||||||
public VersionsController(VersionService versionService)
|
|
||||||
{
|
|
||||||
VersionService = versionService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<ActionResult<VersionDto[]>> GetAsync()
|
|
||||||
{
|
|
||||||
var versions = await VersionService.GetVersionsAsync();
|
|
||||||
return VersionMapper.ToDtos(versions).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("instance")]
|
|
||||||
public async Task<ActionResult<VersionDto>> GetInstanceAsync()
|
|
||||||
{
|
|
||||||
var version = await VersionService.GetInstanceVersionAsync();
|
|
||||||
return VersionMapper.ToDto(version);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("latest")]
|
|
||||||
public async Task<ActionResult<VersionDto>> GetLatestAsync()
|
|
||||||
{
|
|
||||||
var version = await VersionService.GetLatestVersionAsync();
|
|
||||||
|
|
||||||
if(version == null)
|
|
||||||
return Problem("Unable to retrieve latest version", statusCode: 404);
|
|
||||||
|
|
||||||
return VersionMapper.ToDto(version);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Auth;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/auth")]
|
|
||||||
public class AuthController : Controller
|
|
||||||
{
|
|
||||||
private readonly IAuthenticationSchemeProvider SchemeProvider;
|
|
||||||
|
|
||||||
public AuthController(IAuthenticationSchemeProvider schemeProvider)
|
|
||||||
{
|
|
||||||
SchemeProvider = schemeProvider;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet]
|
|
||||||
public async Task<ActionResult<SchemeDto[]>> GetSchemesAsync()
|
|
||||||
{
|
|
||||||
var schemes = await SchemeProvider.GetAllSchemesAsync();
|
|
||||||
|
|
||||||
return schemes
|
|
||||||
.Where(scheme => !string.IsNullOrWhiteSpace(scheme.DisplayName))
|
|
||||||
.Select(scheme => new SchemeDto(scheme.Name, scheme.DisplayName!))
|
|
||||||
.ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("{schemeName:alpha}")]
|
|
||||||
public async Task<ActionResult> StartAsync([FromRoute] string schemeName)
|
|
||||||
{
|
|
||||||
var scheme = await SchemeProvider.GetSchemeAsync(schemeName);
|
|
||||||
|
|
||||||
if (scheme == null || string.IsNullOrWhiteSpace(scheme.DisplayName))
|
|
||||||
return Problem("Invalid authentication scheme name", statusCode: 400);
|
|
||||||
|
|
||||||
return Challenge(new AuthenticationProperties()
|
|
||||||
{
|
|
||||||
RedirectUri = "/"
|
|
||||||
}, scheme.Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
[Authorize]
|
|
||||||
[HttpGet("claims")]
|
|
||||||
public Task<ActionResult<ClaimDto[]>> GetClaimsAsync()
|
|
||||||
{
|
|
||||||
var result = User.Claims
|
|
||||||
.Select(claim => new ClaimDto(claim.Type, claim.Value))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
return Task.FromResult<ActionResult<ClaimDto[]>>(result);
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("logout")]
|
|
||||||
public Task<ActionResult> LogoutAsync()
|
|
||||||
{
|
|
||||||
return Task.FromResult<ActionResult>(
|
|
||||||
SignOut(new AuthenticationProperties()
|
|
||||||
{
|
|
||||||
RedirectUri = "/"
|
|
||||||
})
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
using Moonlight.Api.Mappers;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.Frontend;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/frontend")]
|
|
||||||
public class FrontendController : Controller
|
|
||||||
{
|
|
||||||
private readonly FrontendService FrontendService;
|
|
||||||
|
|
||||||
public FrontendController(FrontendService frontendService)
|
|
||||||
{
|
|
||||||
FrontendService = frontendService;
|
|
||||||
}
|
|
||||||
|
|
||||||
[HttpGet("config")]
|
|
||||||
public async Task<ActionResult<FrontendConfigDto>> GetConfigAsync()
|
|
||||||
{
|
|
||||||
var configuration = await FrontendService.GetConfigurationAsync();
|
|
||||||
return FrontendConfigMapper.ToDto(configuration);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Controllers;
|
|
||||||
|
|
||||||
[ApiController]
|
|
||||||
[Route("api/ping")]
|
|
||||||
public class PingController : Controller
|
|
||||||
{
|
|
||||||
[HttpGet]
|
|
||||||
[AllowAnonymous]
|
|
||||||
public IActionResult Get() => Ok("Pong");
|
|
||||||
}
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
using System.Text.Json.Serialization;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Services.ContainerHelper.Events;
|
|
||||||
|
|
||||||
public struct RebuildEventDto
|
|
||||||
{
|
|
||||||
[JsonPropertyName("type")]
|
|
||||||
public RebuildEventType Type { get; set; }
|
|
||||||
|
|
||||||
[JsonPropertyName("data")]
|
|
||||||
public string Data { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RebuildEventType
|
|
||||||
{
|
|
||||||
Log = 0,
|
|
||||||
Failed = 1,
|
|
||||||
Succeeded = 2,
|
|
||||||
Step = 3
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
namespace Moonlight.Api.Http.Services.ContainerHelper;
|
|
||||||
|
|
||||||
public class ProblemDetails
|
|
||||||
{
|
|
||||||
public string Type { get; set; }
|
|
||||||
public string Title { get; set; }
|
|
||||||
public int Status { get; set; }
|
|
||||||
public string? Detail { get; set; }
|
|
||||||
public Dictionary<string, string[]>? Errors { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Moonlight.Api.Http.Services.ContainerHelper.Requests;
|
|
||||||
|
|
||||||
public record RequestRebuildDto(bool NoBuildCache);
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
namespace Moonlight.Api.Http.Services.ContainerHelper.Requests;
|
|
||||||
|
|
||||||
public record SetVersionDto(string Version);
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
using System.Text.Json;
|
|
||||||
using System.Text.Json.Serialization;
|
|
||||||
using Moonlight.Api.Http.Services.ContainerHelper.Events;
|
|
||||||
using Moonlight.Api.Http.Services.ContainerHelper.Requests;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Http.Services.ContainerHelper;
|
|
||||||
|
|
||||||
[JsonSerializable(typeof(SetVersionDto))]
|
|
||||||
[JsonSerializable(typeof(ProblemDetails))]
|
|
||||||
[JsonSerializable(typeof(RebuildEventDto))]
|
|
||||||
[JsonSerializable(typeof(RequestRebuildDto))]
|
|
||||||
|
|
||||||
[JsonSourceGenerationOptions(JsonSerializerDefaults.Web)]
|
|
||||||
public partial class SerializationContext : JsonSerializerContext
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,79 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using System.Text.Encodings.Web;
|
|
||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
using Microsoft.EntityFrameworkCore;
|
|
||||||
using Microsoft.Extensions.Caching.Hybrid;
|
|
||||||
using Microsoft.Extensions.Logging;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Moonlight.Api.Database;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Implementations.ApiKeyScheme;
|
|
||||||
|
|
||||||
public class ApiKeySchemeHandler : AuthenticationHandler<ApiKeySchemeOptions>
|
|
||||||
{
|
|
||||||
private readonly DatabaseRepository<ApiKey> ApiKeyRepository;
|
|
||||||
private readonly HybridCache HybridCache;
|
|
||||||
|
|
||||||
public const string CacheKeyFormat = $"Moonlight.Api.{nameof(ApiKeySchemeHandler)}.{{0}}";
|
|
||||||
|
|
||||||
public ApiKeySchemeHandler(
|
|
||||||
IOptionsMonitor<ApiKeySchemeOptions> options,
|
|
||||||
ILoggerFactory logger,
|
|
||||||
UrlEncoder encoder,
|
|
||||||
DatabaseRepository<ApiKey> apiKeyRepository,
|
|
||||||
HybridCache hybridCache
|
|
||||||
) : base(options, logger, encoder)
|
|
||||||
{
|
|
||||||
ApiKeyRepository = apiKeyRepository;
|
|
||||||
HybridCache = hybridCache;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
|
||||||
{
|
|
||||||
var authHeaderValue = Request.Headers.Authorization.FirstOrDefault() ?? null;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(authHeaderValue))
|
|
||||||
return AuthenticateResult.NoResult();
|
|
||||||
|
|
||||||
if (authHeaderValue.Length > 32)
|
|
||||||
return AuthenticateResult.Fail("Invalid api key specified");
|
|
||||||
|
|
||||||
var cacheKey = string.Format(CacheKeyFormat, authHeaderValue);
|
|
||||||
|
|
||||||
var apiKey = await HybridCache.GetOrCreateAsync<ApiKeySession?>(
|
|
||||||
cacheKey,
|
|
||||||
async ct =>
|
|
||||||
{
|
|
||||||
return await ApiKeyRepository
|
|
||||||
.Query()
|
|
||||||
.Where(x => x.Key == authHeaderValue)
|
|
||||||
.Select(x => new ApiKeySession(x.Permissions, x.ValidUntil))
|
|
||||||
.FirstOrDefaultAsync(cancellationToken: ct);
|
|
||||||
},
|
|
||||||
new HybridCacheEntryOptions()
|
|
||||||
{
|
|
||||||
LocalCacheExpiration = Options.LookupL1CacheTime,
|
|
||||||
Expiration = Options.LookupL2CacheTime
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (apiKey == null)
|
|
||||||
return AuthenticateResult.Fail("Invalid api key specified");
|
|
||||||
|
|
||||||
if (DateTimeOffset.UtcNow > apiKey.ValidUntil)
|
|
||||||
return AuthenticateResult.Fail("Api key expired");
|
|
||||||
|
|
||||||
return AuthenticateResult.Success(new AuthenticationTicket(
|
|
||||||
new ClaimsPrincipal(
|
|
||||||
new ClaimsIdentity(
|
|
||||||
apiKey.Permissions.Select(x => new Claim(Permissions.ClaimType, x)).ToArray()
|
|
||||||
)
|
|
||||||
),
|
|
||||||
Scheme.Name
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
private record ApiKeySession(string[] Permissions, DateTimeOffset ValidUntil);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authentication;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Implementations.ApiKeyScheme;
|
|
||||||
|
|
||||||
public class ApiKeySchemeOptions : AuthenticationSchemeOptions
|
|
||||||
{
|
|
||||||
public TimeSpan LookupL1CacheTime { get; set; }
|
|
||||||
public TimeSpan LookupL2CacheTime { get; set; }
|
|
||||||
}
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Implementations;
|
|
||||||
|
|
||||||
public class PermissionAuthorizationHandler : AuthorizationHandler<PermissionRequirement>
|
|
||||||
{
|
|
||||||
protected override Task HandleRequirementAsync(
|
|
||||||
AuthorizationHandlerContext context,
|
|
||||||
PermissionRequirement requirement)
|
|
||||||
{
|
|
||||||
var permissionClaim = context.User.FindFirst(x =>
|
|
||||||
x.Type.Equals(Permissions.ClaimType, StringComparison.OrdinalIgnoreCase) &&
|
|
||||||
x.Value.Equals(requirement.Identifier, StringComparison.OrdinalIgnoreCase)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (permissionClaim == null)
|
|
||||||
{
|
|
||||||
context.Fail(new AuthorizationFailureReason(
|
|
||||||
this,
|
|
||||||
$"User does not have the requested permission '{requirement.Identifier}'"
|
|
||||||
));
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
|
|
||||||
context.Succeed(requirement);
|
|
||||||
return Task.CompletedTask;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
using Microsoft.AspNetCore.Authorization;
|
|
||||||
using Microsoft.Extensions.Options;
|
|
||||||
using Moonlight.Shared;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Implementations;
|
|
||||||
|
|
||||||
public class PermissionPolicyProvider : IAuthorizationPolicyProvider
|
|
||||||
{
|
|
||||||
private readonly DefaultAuthorizationPolicyProvider FallbackProvider;
|
|
||||||
|
|
||||||
public PermissionPolicyProvider(IOptions<AuthorizationOptions> options)
|
|
||||||
{
|
|
||||||
FallbackProvider = new DefaultAuthorizationPolicyProvider(options);
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<AuthorizationPolicy?> GetPolicyAsync(string policyName)
|
|
||||||
{
|
|
||||||
if (!policyName.StartsWith(Permissions.Prefix, StringComparison.OrdinalIgnoreCase))
|
|
||||||
return await FallbackProvider.GetPolicyAsync(policyName);
|
|
||||||
|
|
||||||
var policy = new AuthorizationPolicyBuilder();
|
|
||||||
policy.AddRequirements(new PermissionRequirement(policyName));
|
|
||||||
|
|
||||||
return policy.Build();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<AuthorizationPolicy> GetDefaultPolicyAsync()
|
|
||||||
=> FallbackProvider.GetDefaultPolicyAsync();
|
|
||||||
|
|
||||||
public Task<AuthorizationPolicy?> GetFallbackPolicyAsync()
|
|
||||||
=> FallbackProvider.GetFallbackPolicyAsync();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PermissionRequirement : IAuthorizationRequirement
|
|
||||||
{
|
|
||||||
public string Identifier { get; }
|
|
||||||
|
|
||||||
public PermissionRequirement(string identifier)
|
|
||||||
{
|
|
||||||
Identifier = identifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
using Moonlight.Api.Interfaces;
|
|
||||||
using Moonlight.Api.Models;
|
|
||||||
using Moonlight.Api.Services;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Implementations;
|
|
||||||
|
|
||||||
public sealed class UpdateDiagnoseProvider : IDiagnoseProvider
|
|
||||||
{
|
|
||||||
private readonly ApplicationService ApplicationService;
|
|
||||||
|
|
||||||
public UpdateDiagnoseProvider(ApplicationService applicationService)
|
|
||||||
{
|
|
||||||
ApplicationService = applicationService;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Task<DiagnoseResult[]> DiagnoseAsync()
|
|
||||||
{
|
|
||||||
if (ApplicationService.IsUpToDate)
|
|
||||||
return Task.FromResult<DiagnoseResult[]>([]);
|
|
||||||
|
|
||||||
return Task.FromResult<DiagnoseResult[]>([
|
|
||||||
new DiagnoseResult(
|
|
||||||
DiagnoseLevel.Warning,
|
|
||||||
"Instance is not up-to-date",
|
|
||||||
["Moonlight", "Update Check"],
|
|
||||||
"Update your moonlight instance to receive bug fixes, new features and security patches. Update button can be found in the overview",
|
|
||||||
null,
|
|
||||||
"/admin",
|
|
||||||
null
|
|
||||||
)
|
|
||||||
]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Moonlight.Api.Models;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Interfaces;
|
|
||||||
|
|
||||||
public interface IDiagnoseProvider
|
|
||||||
{
|
|
||||||
public Task<DiagnoseResult[]> DiagnoseAsync();
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Security.Claims;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Interfaces;
|
|
||||||
|
|
||||||
public interface IUserAuthHook
|
|
||||||
{
|
|
||||||
public Task<bool> SyncAsync(ClaimsPrincipal principal, User trackedUser);
|
|
||||||
|
|
||||||
// Every implementation of this function should execute as fast as possible
|
|
||||||
// as this directly impacts every api call
|
|
||||||
public Task<bool> ValidateAsync(ClaimsPrincipal principal, int userId);
|
|
||||||
}
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Interfaces;
|
|
||||||
|
|
||||||
public interface IUserDeletionHook
|
|
||||||
{
|
|
||||||
public Task<bool> ValidateAsync(User user, List<string> errors);
|
|
||||||
public Task ExecuteAsync(User user);
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Interfaces;
|
|
||||||
|
|
||||||
public interface IUserLogoutHook
|
|
||||||
{
|
|
||||||
public Task ExecuteAsync(User user);
|
|
||||||
}
|
|
||||||
@@ -1,21 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Moonlight.Api.Database.Entities;
|
|
||||||
using Moonlight.Shared.Http.Requests.Admin.ApiKeys;
|
|
||||||
using Moonlight.Shared.Http.Responses.Admin.ApiKeys;
|
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Mappers;
|
|
||||||
|
|
||||||
[Mapper]
|
|
||||||
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]
|
|
||||||
[SuppressMessage("Mapper", "RMG012:No members are mapped in an object mapping")]
|
|
||||||
public static partial class ApiKeyMapper
|
|
||||||
{
|
|
||||||
public static partial IQueryable<ApiKeyDto> ProjectToDto(this IQueryable<ApiKey> apiKeys);
|
|
||||||
|
|
||||||
public static partial ApiKeyDto ToDto(ApiKey apiKey);
|
|
||||||
|
|
||||||
public static partial void Merge([MappingTarget] ApiKey apiKey, UpdateApiKeyDto request);
|
|
||||||
|
|
||||||
public static partial ApiKey ToEntity(CreateApiKeyDto request);
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Moonlight.Shared.Http.Events;
|
|
||||||
using Riok.Mapperly.Abstractions;
|
|
||||||
|
|
||||||
namespace Moonlight.Api.Mappers;
|
|
||||||
|
|
||||||
[Mapper]
|
|
||||||
[SuppressMessage("Mapper", "RMG020:No members are mapped in an object mapping")]
|
|
||||||
[SuppressMessage("Mapper", "RMG012:No members are mapped in an object mapping")]
|
|
||||||
public static partial class ContainerHelperMapper
|
|
||||||
{
|
|
||||||
public static partial RebuildEventDto ToDto(Http.Services.ContainerHelper.Events.RebuildEventDto rebuildEventDto);
|
|
||||||
}
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user