mirror of
https://github.com/LBPUnion/ProjectLighthouse.git
synced 2025-07-24 14:11:29 +00:00
Merge branch 'refs/heads/main' into digest-refactor
# Conflicts: # ProjectLighthouse.Servers.GameServer/Controllers/Slots/ReviewController.cs
This commit is contained in:
commit
0e0a900f67
86 changed files with 1477 additions and 170 deletions
|
@ -3,7 +3,7 @@
|
|||
"isRoot": true,
|
||||
"tools": {
|
||||
"dotnet-ef": {
|
||||
"version": "7.0.13",
|
||||
"version": "8.0.6",
|
||||
"commands": [
|
||||
"dotnet-ef"
|
||||
]
|
||||
|
|
2
.gitattributes
vendored
Normal file
2
.gitattributes
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
# Normalize shell scripts to have LF line endings
|
||||
*.sh text eol=lf
|
89
.github/workflows/docker-publish.yml
vendored
Normal file
89
.github/workflows/docker-publish.yml
vendored
Normal file
|
@ -0,0 +1,89 @@
|
|||
name: Build Docker Image
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
env:
|
||||
# Use docker.io for Docker Hub if empty
|
||||
REGISTRY: ghcr.io
|
||||
# github.repository as <account>/<repo>
|
||||
IMAGE_NAME: ${{ github.repository }}
|
||||
|
||||
|
||||
jobs:
|
||||
build-publish:
|
||||
name: Build and Publish
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
# This is used to complete the identity challenge
|
||||
# with sigstore/fulcio when running outside of PRs.
|
||||
id-token: write
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
|
||||
# Install the cosign tool except on PR
|
||||
# https://github.com/sigstore/cosign-installer
|
||||
- name: Install cosign
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: sigstore/cosign-installer@e1523de7571e31dbe865fd2e80c5c7c23ae71eb4 #v3.4.0
|
||||
with:
|
||||
cosign-release: 'v2.2.3'
|
||||
|
||||
# Set up BuildKit Docker container builder to be able to build
|
||||
# multi-platform images and export cache
|
||||
# https://github.com/docker/setup-buildx-action
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@f95db51fddba0c2d1ec667646a06c2ce06100226 # v3.0.0
|
||||
|
||||
# Login against a Docker registry except on PR
|
||||
# https://github.com/docker/login-action
|
||||
- name: Log into registry ${{ env.REGISTRY }}
|
||||
if: github.event_name != 'pull_request'
|
||||
uses: docker/login-action@343f7c4344506bcbf9b4de18042ae17996df046d # v3.0.0
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
# Extract metadata (tags, labels) for Docker
|
||||
# https://github.com/docker/metadata-action
|
||||
- name: Extract Docker metadata
|
||||
id: meta
|
||||
uses: docker/metadata-action@96383f45573cb7f253c731d3b3ab81c87ef81934 # v5.0.0
|
||||
with:
|
||||
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
|
||||
|
||||
# Build and push Docker image with Buildx (don't push on PR)
|
||||
# https://github.com/docker/build-push-action
|
||||
- name: Build and push Docker image
|
||||
id: build-and-push
|
||||
uses: docker/build-push-action@0565240e2d4ab88bba5387d719585280857ece09 # v5.0.0
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
cache-from: type=gha
|
||||
cache-to: type=gha,mode=max
|
||||
|
||||
# Sign the resulting Docker image digest except on PRs.
|
||||
# This will only write to the public Rekor transparency log when the Docker
|
||||
# repository is public to avoid leaking data. If you would like to publish
|
||||
# transparency data even for private images, pass --force to cosign below.
|
||||
# https://github.com/sigstore/cosign
|
||||
- name: Sign the published Docker image
|
||||
if: ${{ github.event_name != 'pull_request' }}
|
||||
env:
|
||||
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
|
||||
TAGS: ${{ steps.meta.outputs.tags }}
|
||||
DIGEST: ${{ steps.build-and-push.outputs.digest }}
|
||||
# This step uses the identity token to provision an ephemeral certificate
|
||||
# against the sigstore community Fulcio instance.
|
||||
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Hvis ikke, bedes du publicere kildekoden et eller andet sted, der er tilgængeligt for dine brugere.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Falls nicht, veröffentliche den Quellcode irgendwo, wo die Nutzer deiner Instanz darauf zugreifen können.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to yer users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Si no es así, por favor publique el código fuente en algún lugar accesible para sus usuarios.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>De lo contrario, publique el código fuente en algún lugar accesible para sus usuarios.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Jos ei, julkaise lähdekoodisi jonnekin, jossa se on käyttäjillesi saatavissa.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Kung hindi, mangyaring ilathala ang source code sa isang lugar na maaring ma-access ng iyong mga user.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Dans le cas contraire, veuillez publier le code source dans un endroit accessible à vos utilisateurs.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>In caso contrario, si prega di pubblicare il codice sorgente da qualche parte accessibile ai propri utenti.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Hvis ikke, vennligst publiser kildekoden et sted som er tilgjengelig for brukerne dine</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Jeśli nie, proszę opublikować kod źródłowy dostępny dla użytkowników.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Se não for o caso, publique o código-fonte em um local acessível aos seus utilizadores.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Если нет, пожалуйста, опубликуйте исходный код в доступном для ваших пользователей месте.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Om inte, vänligen publicera källkoden någonstans tillgängligt för dina användare.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Eğer değilse, lütfen kaynak kodunu kullanıcılarınızın erişebileceği bir yerde yayınlayın.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>Якщо ні, будь ласка, опублікуйте вихідний код у місці, доступному для ваших користувачів.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -80,4 +80,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -87,4 +87,10 @@
|
|||
<data name="license_warn_3" xml:space="preserve">
|
||||
<value>If not, please publish the source code somewhere accessible to your users.</value>
|
||||
</data>
|
||||
<data name="read_only_warn_title" xml:space="preserve">
|
||||
<value>Read-Only Mode</value>
|
||||
</data>
|
||||
<data name="read_only_warn" xml:space="preserve">
|
||||
<value>This instance is currently in read-only mode. Level and photo uploads, comments, reviews, and certain profile changes will be restricted until read-only mode is disabled.</value>
|
||||
</data>
|
||||
</root>
|
|
@ -23,5 +23,8 @@ public static class BaseLayoutStrings
|
|||
public static readonly TranslatableString LicenseWarn2 = create("license_warn_2");
|
||||
public static readonly TranslatableString LicenseWarn3 = create("license_warn_3");
|
||||
|
||||
public static readonly TranslatableString ReadOnlyWarnTitle = create("read_only_warn_title");
|
||||
public static readonly TranslatableString ReadOnlyWarn = create("read_only_warn");
|
||||
|
||||
private static TranslatableString create(string key) => new(TranslationAreas.BaseLayout, key);
|
||||
}
|
|
@ -114,6 +114,9 @@ public class CommentController : GameController
|
|||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
GameComment? comment = await this.DeserializeBody<GameComment>();
|
||||
if (comment?.Message == null) return this.BadRequest();
|
||||
|
||||
|
@ -154,6 +157,9 @@ public class CommentController : GameController
|
|||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
if ((slotId == 0 || SlotHelper.IsTypeInvalid(slotType)) == (username == null)) return this.BadRequest();
|
||||
|
||||
CommentEntity? comment = await this.database.Comments.FirstOrDefaultAsync(c => c.CommentId == commentId);
|
||||
|
|
|
@ -71,7 +71,7 @@ public class LoginController : ControllerBase
|
|||
switch (npTicket.Platform)
|
||||
{
|
||||
case Platform.RPCS3:
|
||||
user = await this.database.Users.FirstOrDefaultAsync(u => u.LinkedRpcnId == npTicket.UserId);
|
||||
user = await this.database.Users.FirstOrDefaultAsync(u => u.LinkedRpcnId == npTicket.UserId);
|
||||
break;
|
||||
case Platform.PS3:
|
||||
case Platform.Vita:
|
||||
|
@ -87,7 +87,7 @@ public class LoginController : ControllerBase
|
|||
// If this user id hasn't been linked to any accounts
|
||||
if (user == null)
|
||||
{
|
||||
// Check if there is an account with that username already
|
||||
// Check if there is an account with that username already
|
||||
UserEntity? targetUsername = await this.database.Users.FirstOrDefaultAsync(u => u.Username == npTicket.Username);
|
||||
if (targetUsername != null)
|
||||
{
|
||||
|
@ -174,7 +174,7 @@ public class LoginController : ControllerBase
|
|||
}
|
||||
|
||||
GameTokenEntity? token = await this.database.GameTokens.Include(t => t.User)
|
||||
.FirstOrDefaultAsync(t => t.UserLocation == ipAddress && t.User.Username == npTicket.Username && t.TicketHash == npTicket.TicketHash);
|
||||
.FirstOrDefaultAsync(t => t.User.Username == npTicket.Username && t.TicketHash == npTicket.TicketHash);
|
||||
|
||||
if (token != null)
|
||||
{
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Text.Json;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
@ -37,6 +38,11 @@ public class MatchController : GameController
|
|||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
await LastContactHelper.SetLastContact(this.database, user, token.GameVersion, token.Platform);
|
||||
|
||||
// Do not allow matchmaking if it has been disabled
|
||||
if (!ServerConfiguration.Instance.Matchmaking.MatchmakingEnabled) return this.BadRequest();
|
||||
|
||||
#region Parse match data
|
||||
|
||||
// Example POST /match: [UpdateMyPlayerData,["Player":"FireGamer9872"]]
|
||||
|
@ -70,15 +76,12 @@ public class MatchController : GameController
|
|||
|
||||
#endregion
|
||||
|
||||
await LastContactHelper.SetLastContact(this.database, user, token.GameVersion, token.Platform);
|
||||
|
||||
#region Process match data
|
||||
|
||||
switch (matchData)
|
||||
{
|
||||
case UpdateMyPlayerData playerData:
|
||||
{
|
||||
MatchHelper.SetUserLocation(user.UserId, token.UserLocation);
|
||||
Room? room = RoomHelper.FindRoomByUser(user.UserId, token.GameVersion, token.Platform, true);
|
||||
|
||||
if (playerData.RoomState != null)
|
||||
|
@ -86,19 +89,13 @@ public class MatchController : GameController
|
|||
room.State = (RoomState)playerData.RoomState;
|
||||
break;
|
||||
}
|
||||
// Check how many people are online in release builds, disabled for debug for ..well debugging.
|
||||
#if DEBUG
|
||||
case FindBestRoom diveInData:
|
||||
#else
|
||||
case FindBestRoom diveInData when MatchHelper.UserLocations.Count > 1:
|
||||
#endif
|
||||
{
|
||||
FindBestRoomResponse? response = RoomHelper.FindBestRoom(this.database,
|
||||
user,
|
||||
token.GameVersion,
|
||||
diveInData.RoomSlot,
|
||||
token.Platform,
|
||||
token.UserLocation);
|
||||
token.Platform);
|
||||
|
||||
if (response == null) return this.NotFound();
|
||||
|
||||
|
@ -108,7 +105,7 @@ public class MatchController : GameController
|
|||
|
||||
return this.Ok($"[{{\"StatusCode\":200}},{serialized}]");
|
||||
}
|
||||
case CreateRoom createRoom when !MatchHelper.UserLocations.IsEmpty:
|
||||
case CreateRoom createRoom:
|
||||
{
|
||||
List<int> users = new();
|
||||
foreach (string playerUsername in createRoom.Players)
|
||||
|
|
|
@ -3,6 +3,8 @@ using LBPUnion.ProjectLighthouse.Configuration;
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Localization;
|
||||
using LBPUnion.ProjectLighthouse.Localization.StringLists;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Types;
|
||||
|
@ -58,10 +60,14 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.";
|
|||
announceText.Replace("%user", username);
|
||||
announceText.Replace("%id", token.UserId.ToString());
|
||||
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
{
|
||||
announceText.Insert(0, BaseLayoutStrings.ReadOnlyWarn.Translate(LocalizationManager.DefaultLang) + "\n\n");
|
||||
}
|
||||
|
||||
#if DEBUG
|
||||
announceText.Append("\n\n---DEBUG INFO---\n" +
|
||||
$"user.UserId: {token.UserId}\n" +
|
||||
$"token.UserLocation: {token.UserLocation}\n" +
|
||||
$"token.GameVersion: {token.GameVersion}\n" +
|
||||
$"token.TicketHash: {token.TicketHash}\n" +
|
||||
$"token.ExpiresAt: {token.ExpiresAt.ToString()}\n" +
|
||||
|
|
|
@ -32,6 +32,9 @@ public class PhotosController : GameController
|
|||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
int photoCount = await this.database.Photos.CountAsync(p => p.CreatorId == token.UserId);
|
||||
if (photoCount >= ServerConfiguration.Instance.UserGeneratedContentLimits.PhotosQuota) return this.BadRequest();
|
||||
|
||||
|
@ -85,7 +88,7 @@ public class PhotosController : GameController
|
|||
case SlotType.Developer:
|
||||
{
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.Type == photoSlot.SlotType && s.InternalSlotId == photoSlot.SlotId);
|
||||
if (slot != null)
|
||||
if (slot != null)
|
||||
photoSlot.SlotId = slot.SlotId;
|
||||
else
|
||||
photoSlot.SlotId = await SlotHelper.GetPlaceholderSlotId(this.database, photoSlot.SlotId, photoSlot.SlotType);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Text;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
|
@ -54,10 +55,14 @@ public class ResourcesController : GameController
|
|||
string fullPath = Path.GetFullPath(path);
|
||||
|
||||
FileHelper.EnsureDirectoryCreated(assetsDirectory);
|
||||
// lbp treats code 409 as success and as an indicator that the file is already present
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
// LBP treats code 409 as success and as an indicator that the file is already present
|
||||
if (FileHelper.ResourceExists(hash)) return this.Conflict();
|
||||
|
||||
// theoretically shouldn't be possible because of hash check but handle anyways
|
||||
// Theoretically shouldn't be possible because of hash check but handle anyways
|
||||
if (!fullPath.StartsWith(FileHelper.FullResourcePath)) return this.BadRequest();
|
||||
|
||||
Logger.Info($"Processing resource upload (hash: {hash})", LogArea.Resources);
|
||||
|
|
|
@ -44,6 +44,9 @@ public class PublishController : GameController
|
|||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
GameUserSlot? slot = await this.DeserializeBody<GameUserSlot>();
|
||||
if (slot == null)
|
||||
{
|
||||
|
@ -117,6 +120,9 @@ public class PublishController : GameController
|
|||
UserEntity? user = await this.database.UserFromGameToken(token);
|
||||
if (user == null) return this.Forbid();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
GameUserSlot? slot = await this.DeserializeBody<GameUserSlot>();
|
||||
|
||||
if (slot == null)
|
||||
|
@ -336,6 +342,9 @@ public class PublishController : GameController
|
|||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
SlotEntity? slot = await this.database.Slots.FirstOrDefaultAsync(s => s.SlotId == id);
|
||||
if (slot == null) return this.NotFound();
|
||||
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
@ -87,6 +89,9 @@ public class ReviewController : GameController
|
|||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
GameReview? newReview = await this.DeserializeBody<GameReview>();
|
||||
if (newReview == null) return this.BadRequest();
|
||||
|
||||
|
@ -110,7 +115,7 @@ public class ReviewController : GameController
|
|||
}
|
||||
review.Thumb = Math.Clamp(newReview.Thumb, -1, 1);
|
||||
review.LabelCollection = LabelHelper.RemoveInvalidLabels(newReview.LabelCollection);
|
||||
|
||||
|
||||
review.Text = newReview.Text;
|
||||
review.Deleted = false;
|
||||
review.Timestamp = TimeHelper.TimestampMillis;
|
||||
|
@ -148,6 +153,13 @@ public class ReviewController : GameController
|
|||
|
||||
List<GameReview> reviews = (await this.database.Reviews
|
||||
.Where(r => r.SlotId == slotId)
|
||||
.Select(r => new
|
||||
{
|
||||
Review = r,
|
||||
SlotVersion = r.Slot!.GameVersion,
|
||||
})
|
||||
.Where(a => a.SlotVersion <= token.GameVersion)
|
||||
.Select(a => a.Review)
|
||||
.OrderByDescending(r => r.ThumbsUp - r.ThumbsDown)
|
||||
.ThenByDescending(r => r.Timestamp)
|
||||
.ApplyPagination(pageData)
|
||||
|
@ -169,6 +181,13 @@ public class ReviewController : GameController
|
|||
|
||||
List<GameReview> reviews = (await this.database.Reviews
|
||||
.Where(r => r.ReviewerId == targetUserId)
|
||||
.Select(r => new
|
||||
{
|
||||
Review = r,
|
||||
SlotVersion = r.Slot!.GameVersion,
|
||||
})
|
||||
.Where(a => a.SlotVersion <= token.GameVersion)
|
||||
.Select(a => a.Review)
|
||||
.OrderByDescending(r => r.Timestamp)
|
||||
.ApplyPagination(pageData)
|
||||
.ToListAsync()).ToSerializableList(r => GameReview.CreateFromEntity(r, token));
|
||||
|
@ -234,6 +253,9 @@ public class ReviewController : GameController
|
|||
{
|
||||
GameTokenEntity token = this.GetToken();
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
int creatorId = await this.database.Slots.Where(s => s.SlotId == slotId).Select(s => s.CreatorId).FirstOrDefaultAsync();
|
||||
if (creatorId == 0) return this.BadRequest();
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Text.Json;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
|
@ -69,6 +70,9 @@ public class UserController : GameController
|
|||
|
||||
if (update.Biography != null)
|
||||
{
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
if (update.Biography.Length > 512) return this.BadRequest();
|
||||
|
||||
user.Biography = update.Biography;
|
||||
|
@ -81,6 +85,9 @@ public class UserController : GameController
|
|||
{
|
||||
if (string.IsNullOrWhiteSpace(resource)) continue;
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.BadRequest();
|
||||
|
||||
if (!FileHelper.ResourceExists(resource) && !resource.StartsWith('g')) return this.BadRequest();
|
||||
|
||||
if (!GameResourceHelper.IsValidTexture(resource)) return this.BadRequest();
|
||||
|
@ -169,7 +176,7 @@ public class UserController : GameController
|
|||
// Sometimes the update gets called periodically as pin progress updates via playing,
|
||||
// may not affect equipped profile pins however, so check before setting it.
|
||||
string currentPins = user.Pins;
|
||||
string newPins = string.Join(",", pinJson.ProfilePins);
|
||||
string newPins = string.Join(",", pinJson.ProfilePins.Distinct());
|
||||
|
||||
if (string.Equals(currentPins, newPins)) return this.Ok("[{\"StatusCode\":200}]");
|
||||
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
using System.Security.Claims;
|
||||
using System.Net;
|
||||
using System.Security.Claims;
|
||||
using System.Text.Encodings.Web;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
@ -36,10 +38,17 @@ public class TokenAuthHandler : AuthenticationHandler<AuthenticationSchemeOption
|
|||
GameTokenEntity? gameToken = await this.database.GameTokenFromRequest(this.Request);
|
||||
if (gameToken == null) return AuthenticateResult.Fail("No game token");
|
||||
|
||||
IPAddress? remoteIpAddress = this.Context.Connection.RemoteIpAddress;
|
||||
if (remoteIpAddress == null) return AuthenticateResult.Fail("Failed to determine IP address");
|
||||
|
||||
if (CryptoHelper.Sha256Hash(remoteIpAddress.ToString()) != gameToken.LocationHash)
|
||||
return AuthenticateResult.Fail("IP address change detected");
|
||||
|
||||
this.Context.Items["Token"] = gameToken;
|
||||
Claim[] claims = {
|
||||
new("userId", gameToken.UserId.ToString()),
|
||||
};
|
||||
Claim[] claims =
|
||||
[
|
||||
new Claim("userId", gameToken.UserId.ToString()),
|
||||
];
|
||||
ClaimsIdentity identity = new(claims, this.Scheme.Name);
|
||||
ClaimsPrincipal principal = new(identity);
|
||||
AuthenticationTicket ticket = new(principal, this.Scheme.Name);
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Extensions;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Logging;
|
||||
using LBPUnion.ProjectLighthouse.Types.Moderation.Cases;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
|
@ -26,7 +29,8 @@ public class AdminUserController : ControllerBase
|
|||
/// Resets the user's earth decorations to a blank state. Useful for users who abuse audio for example.
|
||||
/// </summary>
|
||||
[HttpGet("wipePlanets")]
|
||||
public async Task<IActionResult> WipePlanets([FromRoute] int id) {
|
||||
public async Task<IActionResult> WipePlanets([FromRoute] int id)
|
||||
{
|
||||
UserEntity? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsModerator) return this.NotFound();
|
||||
|
||||
|
@ -90,6 +94,78 @@ public class AdminUserController : ControllerBase
|
|||
|
||||
return this.Redirect($"/user/{targetedUser.UserId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes every comment by the user. Useful in case of mass spam
|
||||
/// </summary>
|
||||
[HttpGet("wipeComments")]
|
||||
public async Task<IActionResult> WipeComments([FromRoute] int id)
|
||||
{
|
||||
UserEntity? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsModerator) return this.NotFound();
|
||||
|
||||
UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||
if (targetedUser == null) return this.NotFound();
|
||||
|
||||
// Find every comment by the user, then set the deletion info on them
|
||||
await this.database.Comments.Where(c => c.PosterUserId == targetedUser.UserId)
|
||||
.ExecuteUpdateAsync(s =>
|
||||
s.SetProperty(c => c.Deleted, true)
|
||||
.SetProperty(c => c.DeletedBy, user.Username)
|
||||
.SetProperty(c => c.DeletedType, "moderator"));
|
||||
Logger.Success($"Deleted comments for {targetedUser.Username} (id:{targetedUser.UserId})", LogArea.Admin);
|
||||
|
||||
await this.database.SendNotification(targetedUser.UserId,
|
||||
"Your comments have been deleted by a moderator.");
|
||||
|
||||
return this.Redirect($"/user/{targetedUser.UserId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Deletes every score from the user. Useful in the case where a user cheated a ton of scores
|
||||
/// </summary>
|
||||
[HttpGet("wipeScores")]
|
||||
public async Task<IActionResult> WipeScores([FromRoute] int id)
|
||||
{
|
||||
UserEntity? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsModerator) return this.NotFound();
|
||||
|
||||
UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||
if (targetedUser == null) return this.NotFound();
|
||||
|
||||
// Find and delete every score uploaded by the target user
|
||||
await this.database.Scores.Where(c => c.UserId == targetedUser.UserId).ExecuteDeleteAsync();
|
||||
Logger.Success($"Deleted scores for {targetedUser.Username} (id:{targetedUser.UserId})", LogArea.Admin);
|
||||
|
||||
await this.database.SendNotification(targetedUser.UserId, "Your scores have been deleted by a moderator.");
|
||||
|
||||
return this.Redirect($"/user/{targetedUser.UserId}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Forces the email verification of a user.
|
||||
/// </summary>
|
||||
[HttpGet("forceVerifyEmail")]
|
||||
public async Task<IActionResult> ForceVerifyEmail([FromRoute] int id)
|
||||
{
|
||||
UserEntity? user = this.database.UserFromWebRequest(this.Request);
|
||||
if (user == null || !user.IsModerator) return this.NotFound();
|
||||
|
||||
UserEntity? targetedUser = await this.database.Users.FirstOrDefaultAsync(u => u.UserId == id);
|
||||
if (targetedUser == null) return this.NotFound();
|
||||
if (targetedUser.EmailAddress == null || targetedUser.EmailAddressVerified) return this.NotFound();
|
||||
|
||||
List<EmailVerificationTokenEntity> tokens = await this.database.EmailVerificationTokens
|
||||
.Where(t => t.UserId == targetedUser.UserId)
|
||||
.ToListAsync();
|
||||
this.database.EmailVerificationTokens.RemoveRange(tokens);
|
||||
|
||||
targetedUser.EmailAddressVerified = true;
|
||||
|
||||
await this.database.SaveChangesAsync();
|
||||
|
||||
return this.Redirect($"/user/{targetedUser.UserId}");
|
||||
}
|
||||
|
||||
[HttpPost("/admin/user/{id:int}/setPermissionLevel")]
|
||||
public async Task<IActionResult> SetUserPermissionLevel([FromRoute] int id, [FromForm] PermissionLevel role)
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
#if DEBUG
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
#endif
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Servers.Website.Controllers.Debug;
|
||||
|
||||
|
@ -27,10 +29,6 @@ public class RoomVisualizerController : ControllerBase
|
|||
List<int> users = await this.database.Users.OrderByDescending(_ => EF.Functions.Random()).Take(2).Select(u => u.UserId).ToListAsync();
|
||||
RoomHelper.CreateRoom(users, GameVersion.LittleBigPlanet2, Platform.PS3);
|
||||
|
||||
foreach (int user in users)
|
||||
{
|
||||
MatchHelper.SetUserLocation(user, "127.0.0.1");
|
||||
}
|
||||
return this.Redirect("/debug/roomVisualizer");
|
||||
#endif
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ using LBPUnion.ProjectLighthouse.Types.Logging;
|
|||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
// I would like to apologize in advance for anyone dealing with this file.
|
||||
// I would like to apologize in advance for anyone dealing with this file.
|
||||
// Theres probably a better way to do this with delegates but I'm tired.
|
||||
// TODO: Clean up this file
|
||||
// - jvyden
|
||||
|
@ -63,6 +63,9 @@ public class SlotPageController : ControllerBase
|
|||
WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request);
|
||||
if (token == null) return this.Redirect("~/login");
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/slot/" + id);
|
||||
|
||||
if (msg == null)
|
||||
{
|
||||
Logger.Error($"Refusing to post comment from {token.UserId} on level {id}, {nameof(msg)} is null", LogArea.Comments);
|
||||
|
|
|
@ -39,6 +39,9 @@ public class UserPageController : ControllerBase
|
|||
WebTokenEntity? token = this.database.WebTokenFromRequest(this.Request);
|
||||
if (token == null) return this.Redirect("~/login");
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode) return this.Redirect("~/user/" + id);
|
||||
|
||||
if (msg == null)
|
||||
{
|
||||
Logger.Error($"Refusing to post comment from {token.UserId} on user {id}, {nameof(msg)} is null", LogArea.Comments);
|
||||
|
|
|
@ -17,15 +17,15 @@
|
|||
|
||||
<script>
|
||||
let shouldRefresh = true;
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
if (shouldRefresh) window.location.reload();
|
||||
}, @(refreshSeconds * 1000));
|
||||
|
||||
|
||||
function stopRefreshing() {
|
||||
shouldRefresh = false;
|
||||
console.log("Stopped refresh");
|
||||
|
||||
|
||||
const stopRefreshButton = document.getElementById("stop-refresh-button");
|
||||
stopRefreshButton.parentElement.removeChild(stopRefreshButton);
|
||||
console.log("Removed stop refresh button");
|
||||
|
@ -61,7 +61,7 @@
|
|||
#nullable enable
|
||||
if (version == GameVersion.LittleBigPlanet1 || version == GameVersion.LittleBigPlanetPSP || version == GameVersion.Unknown) continue;
|
||||
|
||||
FindBestRoomResponse? response = RoomHelper.FindBestRoom(Database, null, version, null, null, null);
|
||||
FindBestRoomResponse? response = RoomHelper.FindBestRoom(Database, null, version, null, null);
|
||||
string text = response == null ? "No room found." : "Room " + response.RoomId;
|
||||
|
||||
<p><b>Best room for @version.ToPrettyString()</b>: @text</p>
|
||||
|
|
|
@ -14,5 +14,4 @@
|
|||
<p><b>Branch</b>: @VersionHelper.Branch</p>
|
||||
<p><b>Build</b>: @VersionHelper.Build</p>
|
||||
<p><b>CommitHash</b>: @VersionHelper.CommitHash</p>
|
||||
<p><b>IsDirty</b>: @VersionHelper.IsDirty</p>
|
||||
<p><b>RepositoryUrl</b>: @VersionHelper.RepositoryUrl</p>
|
|
@ -60,7 +60,37 @@
|
|||
}
|
||||
}
|
||||
|
||||
<br><br>
|
||||
@if (Model.LatestAnnouncement != null)
|
||||
{
|
||||
<div class="ui blue segment" style="position: relative;">
|
||||
<div>
|
||||
<h3>@Model.LatestAnnouncement.Title</h3>
|
||||
<div style="padding-bottom: 2em;">
|
||||
@if (Model.LatestAnnouncement.Content.Length > 250)
|
||||
{
|
||||
<span style="white-space: pre-line">@Model.LatestAnnouncement.Content[..250]...<a href="@ServerConfiguration.Instance.ExternalUrl/notifications">read more</a></span>
|
||||
}
|
||||
else
|
||||
{
|
||||
<span style="white-space: pre-line">@Model.LatestAnnouncement.Content</span>
|
||||
}
|
||||
</div>
|
||||
@if (Model.LatestAnnouncement.Publisher != null)
|
||||
{
|
||||
<div class="ui tiny bottom left attached label">
|
||||
Posted by
|
||||
<a style="color: black" href="~/user/@Model.LatestAnnouncement.Publisher.UserId">
|
||||
@Model.LatestAnnouncement.Publisher.Username
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<br /><br />
|
||||
}
|
||||
|
||||
<div class="@(isMobile ? "" : "ui center aligned grid")">
|
||||
<div class="eight wide column">
|
||||
|
|
|
@ -5,6 +5,7 @@ using LBPUnion.ProjectLighthouse.Helpers;
|
|||
using LBPUnion.ProjectLighthouse.Servers.Website.Pages.Layouts;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Website;
|
||||
using LBPUnion.ProjectLighthouse.Types.Levels;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
@ -19,6 +20,8 @@ public class LandingPage : BaseLayout
|
|||
public int PendingAuthAttempts;
|
||||
public List<UserEntity> PlayersOnline = new();
|
||||
|
||||
public WebsiteAnnouncementEntity? LatestAnnouncement;
|
||||
|
||||
public LandingPage(DatabaseContext database) : base(database)
|
||||
{ }
|
||||
|
||||
|
@ -54,6 +57,10 @@ public class LandingPage : BaseLayout
|
|||
.Include(s => s.Creator)
|
||||
.ToListAsync();
|
||||
|
||||
this.LatestAnnouncement = await this.Database.WebsiteAnnouncements.Include(a => a.Publisher)
|
||||
.OrderByDescending(a => a.AnnouncementId)
|
||||
.FirstOrDefaultAsync();
|
||||
|
||||
return this.Page();
|
||||
}
|
||||
}
|
|
@ -158,22 +158,14 @@
|
|||
</div>
|
||||
</div>
|
||||
</noscript>
|
||||
@if (!ServerStatics.IsDebug() && VersionHelper.IsDirty)
|
||||
@if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
{
|
||||
<div class="ui bottom attached red message large">
|
||||
<div class="ui container">
|
||||
<i class="warning icon"></i>
|
||||
<span style="font-size: 1.2rem;">@Model.Translate(BaseLayoutStrings.LicenseWarnTitle)</span>
|
||||
<span style="font-size: 1.2rem;">@Model.Translate(BaseLayoutStrings.ReadOnlyWarnTitle)</span>
|
||||
<p>
|
||||
@Html.Raw(Model.Translate(BaseLayoutStrings.LicenseWarn1,
|
||||
"<a href=\"https://github.com/LBPUnion/project-lighthouse/blob/main/LICENSE\">GNU Affero General Public License v3.0</a>"))
|
||||
</p>
|
||||
<p>
|
||||
@Html.Raw(Model.Translate(BaseLayoutStrings.LicenseWarn2,
|
||||
"<code>git status</code>", "<a href=\"https://github.com/LBPUnion/project-lighthouse/issues\">", "</a>"))
|
||||
</p>
|
||||
<p>
|
||||
@Html.Raw(Model.Translate(BaseLayoutStrings.LicenseWarn3))
|
||||
@Html.Raw(Model.Translate(BaseLayoutStrings.ReadOnlyWarn))
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -222,10 +214,6 @@
|
|||
<span class="ui text red">Cannot Detect Source Code</span>
|
||||
}
|
||||
</span>
|
||||
@if (VersionHelper.IsDirty)
|
||||
{
|
||||
<p>@Model.Translate(BaseLayoutStrings.GeneratedModified)</p>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
@if (ServerStatics.IsDebug())
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
@using System.Web
|
||||
@using LBPUnion.ProjectLighthouse.Configuration
|
||||
@using LBPUnion.ProjectLighthouse.Database
|
||||
@using LBPUnion.ProjectLighthouse.Localization
|
||||
@using LBPUnion.ProjectLighthouse.Servers.Website.Extensions
|
||||
|
@ -31,18 +32,32 @@
|
|||
@if (Model.CommentsEnabled && Model.User != null)
|
||||
{
|
||||
<div class="ui divider"></div>
|
||||
<form class="ui reply form" action="postComment" method="post">
|
||||
<div class="field">
|
||||
<textarea style="min-height: 70px; height: 70px; max-height:120px" maxlength="100" name="msg"></textarea>
|
||||
@if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
{
|
||||
<div class="ui red segment">
|
||||
<p>
|
||||
<i>
|
||||
@ServerConfiguration.Instance.Customization.ServerName is currently in read-only mode.
|
||||
You will not be able to post comments until read-only mode is disabled.
|
||||
</i>
|
||||
</p>
|
||||
</div>
|
||||
<input type="submit" class="ui blue button">
|
||||
</form>
|
||||
}
|
||||
else
|
||||
{
|
||||
<form class="ui reply form" action="postComment" method="post">
|
||||
<div class="field">
|
||||
<textarea style="min-height: 70px; height: 70px; max-height:120px" maxlength="100" name="msg"></textarea>
|
||||
</div>
|
||||
<input type="submit" class="ui blue button">
|
||||
</form>
|
||||
}
|
||||
@if (Model.Comments.Count > 0)
|
||||
{
|
||||
<div class="ui divider"></div>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@{
|
||||
int i = 0;
|
||||
foreach (KeyValuePair<CommentEntity, RatedCommentEntity?> commentAndReaction in Model.Comments)
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
@using LBPUnion.ProjectLighthouse.Helpers
|
||||
@using LBPUnion.ProjectLighthouse.Types.Entities.Level
|
||||
@using LBPUnion.ProjectLighthouse.Types.Serialization
|
||||
|
||||
@{
|
||||
bool isMobile = (bool?)ViewData["IsMobile"] ?? false;
|
||||
bool canDelete = (bool?)ViewData["CanDelete"] ?? false;
|
||||
|
@ -29,6 +28,18 @@
|
|||
<div class="ui divider"></div>
|
||||
}
|
||||
|
||||
@if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
{
|
||||
<div class="ui red segment">
|
||||
<p>
|
||||
<i>
|
||||
@ServerConfiguration.Instance.Customization.ServerName is currently in read-only mode.
|
||||
You will not be able to post reviews in-game until read-only mode is disabled.
|
||||
</i>
|
||||
</p>
|
||||
</div>
|
||||
}
|
||||
|
||||
@for(int i = 0; i < Model.Reviews.Count; i++)
|
||||
{
|
||||
ReviewEntity review = Model.Reviews[i];
|
||||
|
@ -36,7 +47,7 @@
|
|||
-1 => review.Reviewer?.BooHash,
|
||||
0 => review.Reviewer?.MehHash,
|
||||
1 => review.Reviewer?.YayHash,
|
||||
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
}) ?? "";
|
||||
|
||||
|
@ -49,7 +60,7 @@
|
|||
-1 => "Boo!",
|
||||
0 => "Meh.",
|
||||
1 => "Yay!",
|
||||
|
||||
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
|
@ -114,7 +125,7 @@
|
|||
if (window.confirm("Are you sure you want to delete this?\nThis action cannot be undone.")){
|
||||
window.location.hash = "reviews";
|
||||
window.location.href = "/moderation/deleteReview/" + reviewId + "?callbackUrl=" + this.window.location;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#nullable enable
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Files;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
@ -25,6 +26,10 @@ public class SlotSettingsPage : BaseLayout
|
|||
|
||||
if (!this.User.IsModerator && this.User != this.Slot.Creator) return this.Redirect("~/slot/" + slotId);
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
return this.Redirect($"~/slot/{slotId}");
|
||||
|
||||
string? avatarHash = await FileHelper.ParseBase64Image(avatar);
|
||||
|
||||
if (avatarHash != null) this.Slot.IconHash = avatarHash;
|
||||
|
@ -46,7 +51,7 @@ public class SlotSettingsPage : BaseLayout
|
|||
if (labels != null)
|
||||
{
|
||||
labels = LabelHelper.RemoveInvalidLabels(labels);
|
||||
if (this.Slot.AuthorLabels != labels)
|
||||
if (this.Slot.AuthorLabels != labels)
|
||||
this.Slot.AuthorLabels = labels;
|
||||
}
|
||||
|
||||
|
|
|
@ -324,6 +324,18 @@ else
|
|||
<i class="trash alternate icon"></i>
|
||||
<span>Wipe Earth Decorations</span>
|
||||
</a>
|
||||
|
||||
<a class="ui red button" href="/moderation/user/@Model.ProfileUser.UserId/wipeComments"
|
||||
onclick="return confirm('Are you sure you want to delete ALL of this user\'s comments? This action cannot be reversed.')">
|
||||
<i class="trash alternate icon"></i>
|
||||
<span>Wipe User's Comments</span>
|
||||
</a>
|
||||
|
||||
<a class="ui red button" href="/moderation/user/@Model.ProfileUser.UserId/wipeScores"
|
||||
onclick="return confirm('Are you sure you want to delete ALL of this user\'s scores? This action cannot be reversed.')">
|
||||
<i class="trash alternate icon"></i>
|
||||
<span>Wipe User's Scores</span>
|
||||
</a>
|
||||
|
||||
@if (!Model.CommentsDisabledByModerator)
|
||||
{
|
||||
|
@ -333,6 +345,14 @@ else
|
|||
</a>
|
||||
}
|
||||
|
||||
@if (Model.ProfileUser.EmailAddress != null && !Model.ProfileUser.EmailAddressVerified)
|
||||
{
|
||||
<a class="ui green button" href="/moderation/user/@Model.ProfileUser.UserId/forceVerifyEmail">
|
||||
<i class="check icon"></i>
|
||||
<span>Forcibly Verify Email</span>
|
||||
</a>
|
||||
}
|
||||
|
||||
@if (Model.User.IsAdmin)
|
||||
{
|
||||
<div class="ui divider"></div>
|
||||
|
|
|
@ -45,7 +45,8 @@ public class UserPage : BaseLayout
|
|||
if (this.ProfileUser == null) return this.NotFound();
|
||||
|
||||
string userSlug = this.ProfileUser.GenerateSlug();
|
||||
if (slug == null || userSlug != slug)
|
||||
// Only redirect if there is a valid slug for this user and the current slug doesn't match
|
||||
if (!string.IsNullOrWhiteSpace(userSlug) && (slug == null || userSlug != slug))
|
||||
{
|
||||
return this.Redirect($"~/user/{userId}/{userSlug}");
|
||||
}
|
||||
|
|
|
@ -39,6 +39,10 @@ public class UserSettingsPage : BaseLayout
|
|||
|
||||
if (!this.User.IsModerator && this.User != this.ProfileUser) return this.Redirect("~/user/" + userId);
|
||||
|
||||
// Deny request if in read-only mode
|
||||
if (avatar != null && ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
return this.Redirect($"~/user/{userId}");
|
||||
|
||||
string? avatarHash = await FileHelper.ParseBase64Image(avatar);
|
||||
|
||||
if (avatarHash != null) this.ProfileUser.IconHash = avatarHash;
|
||||
|
@ -47,6 +51,10 @@ public class UserSettingsPage : BaseLayout
|
|||
|
||||
if (biography != null)
|
||||
{
|
||||
// Deny request if in read-only mode
|
||||
if (ServerConfiguration.Instance.UserGeneratedContentLimits.ReadOnlyMode)
|
||||
return this.Redirect($"~/user/{userId}");
|
||||
|
||||
biography = CensorHelper.FilterMessage(biography);
|
||||
if (this.ProfileUser.Biography != biography && biography.Length <= 512)
|
||||
this.ProfileUser.Biography = biography;
|
||||
|
|
|
@ -11,6 +11,6 @@
|
|||
<ItemGroup>
|
||||
<ProjectReference Include="..\ProjectLighthouse.Localization\ProjectLighthouse.Localization.csproj" />
|
||||
<ProjectReference Include="..\ProjectLighthouse\ProjectLighthouse.csproj" />
|
||||
<PackageReference Include="QRCoder" Version="1.4.3" />
|
||||
<PackageReference Include="QRCoder" Version="1.5.1" />
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
|
|
@ -20,8 +20,8 @@ public class DatabaseTests : LighthouseServerTest<GameServerTestStartup>
|
|||
|
||||
int rand = new Random().Next();
|
||||
|
||||
UserEntity userA = await database.CreateUser("unitTestUser" + rand, CryptoHelper.GenerateAuthToken());
|
||||
UserEntity userB = await database.CreateUser("unitTestUser" + rand, CryptoHelper.GenerateAuthToken());
|
||||
UserEntity userA = await database.CreateUser("unitTestUser" + rand, CryptoHelper.BCryptHash(CryptoHelper.GenerateAuthToken()));
|
||||
UserEntity userB = await database.CreateUser("unitTestUser" + rand, CryptoHelper.BCryptHash(CryptoHelper.GenerateAuthToken()));
|
||||
|
||||
Assert.NotNull(userA);
|
||||
Assert.NotNull(userB);
|
||||
|
|
|
@ -9,18 +9,18 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.8.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
|
|
@ -0,0 +1,100 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Servers.GameServer.Controllers.Slots;
|
||||
using LBPUnion.ProjectLighthouse.Tests.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Level;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Token;
|
||||
using LBPUnion.ProjectLighthouse.Types.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Xunit;
|
||||
|
||||
namespace ProjectLighthouse.Tests.GameApiTests.Unit.Controllers;
|
||||
|
||||
[Trait("Category", "Unit")]
|
||||
public class ReviewControllerTests
|
||||
{
|
||||
private static async Task InsertTestData(DatabaseContext database)
|
||||
{
|
||||
database.Slots.Add(new SlotEntity
|
||||
{
|
||||
SlotId = 1,
|
||||
CreatorId = 1,
|
||||
GameVersion = GameVersion.LittleBigPlanet3,
|
||||
});
|
||||
|
||||
database.Slots.Add(new SlotEntity
|
||||
{
|
||||
SlotId = 2,
|
||||
CreatorId = 1,
|
||||
GameVersion = GameVersion.LittleBigPlanet2,
|
||||
});
|
||||
|
||||
database.Reviews.Add(new ReviewEntity
|
||||
{
|
||||
ReviewId = 1,
|
||||
ReviewerId = 1,
|
||||
SlotId = 1,
|
||||
});
|
||||
|
||||
database.Reviews.Add(new ReviewEntity
|
||||
{
|
||||
ReviewId = 2,
|
||||
ReviewerId = 1,
|
||||
SlotId = 2,
|
||||
});
|
||||
await database.SaveChangesAsync();
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(GameVersion.LittleBigPlanet2, 1)]
|
||||
[InlineData(GameVersion.LittleBigPlanet3, 2)]
|
||||
public async Task ReviewsBy_ShouldNotList_HigherGameVersions(GameVersion version, int expected)
|
||||
{
|
||||
GameTokenEntity token = MockHelper.GetUnitTestToken();
|
||||
token.GameVersion = version;
|
||||
DatabaseContext database = await MockHelper.GetTestDatabase(new List<GameTokenEntity>
|
||||
{
|
||||
token,
|
||||
});
|
||||
|
||||
await InsertTestData(database);
|
||||
|
||||
ReviewController controller = new(database);
|
||||
controller.SetupTestController(token);
|
||||
|
||||
IActionResult response = await controller.ReviewsBy("unittest");
|
||||
ReviewResponse review = response.CastTo<OkObjectResult, ReviewResponse>();
|
||||
|
||||
Assert.Equal(expected, review.Reviews.Count);
|
||||
Assert.True(review.Reviews.All(r => database.Slots.FirstOrDefault(s => s.SlotId == r.Slot.SlotId)?.GameVersion <= version));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(GameVersion.LittleBigPlanet2, 2, 1)]
|
||||
[InlineData(GameVersion.LittleBigPlanet2, 1, 0)]
|
||||
[InlineData(GameVersion.LittleBigPlanet3, 2, 1)]
|
||||
[InlineData(GameVersion.LittleBigPlanet3, 1, 1)]
|
||||
public async Task ReviewsFor_ShouldNotList_HigherGameVersions(GameVersion version, int slotId, int expected)
|
||||
{
|
||||
GameTokenEntity token = MockHelper.GetUnitTestToken();
|
||||
token.GameVersion = version;
|
||||
DatabaseContext database = await MockHelper.GetTestDatabase(new List<GameTokenEntity>
|
||||
{
|
||||
token,
|
||||
});
|
||||
|
||||
await InsertTestData(database);
|
||||
|
||||
ReviewController controller = new(database);
|
||||
controller.SetupTestController(token);
|
||||
|
||||
IActionResult response = await controller.ReviewsFor(slotId);
|
||||
ReviewResponse review = response.CastTo<OkObjectResult, ReviewResponse>();
|
||||
|
||||
Assert.Equal(expected, review.Reviews.Count);
|
||||
Assert.True(review.Reviews.All(r => database.Slots.FirstOrDefault(s => s.SlotId == r.Slot.SlotId)?.GameVersion <= version));
|
||||
}
|
||||
}
|
|
@ -177,4 +177,29 @@ public class UserControllerTests
|
|||
Assert.Equal(expectedPins, dbMock.Users.First().Pins);
|
||||
Assert.Equal(expectedResponse, pinsResponse);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task UpdateMyPins_ShouldRemove_DuplicatePins()
|
||||
{
|
||||
UserEntity entity = MockHelper.GetUnitTestUser();
|
||||
entity.Pins = "1234";
|
||||
List<UserEntity> users = new()
|
||||
{
|
||||
entity,
|
||||
};
|
||||
await using DatabaseContext dbMock = await MockHelper.GetTestDatabase(users);
|
||||
|
||||
UserController userController = new(dbMock);
|
||||
userController.SetupTestController("{\"profile_pins\": [1234, 1234]}");
|
||||
|
||||
const string expectedPins = "1234";
|
||||
const string expectedResponse = "[{\"StatusCode\":200}]";
|
||||
|
||||
IActionResult result = await userController.UpdateMyPins();
|
||||
|
||||
string pinsResponse = result.CastTo<OkObjectResult, string>();
|
||||
|
||||
Assert.Equal(expectedPins, dbMock.Users.First().Pins);
|
||||
Assert.Equal(expectedResponse, pinsResponse);
|
||||
}
|
||||
}
|
|
@ -9,20 +9,20 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.17.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="121.0.6167.8500" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.21.0" />
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="125.0.6422.14100" />
|
||||
<PackageReference Include="xunit" Version="2.8.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
|
|
|
@ -37,7 +37,6 @@ public static class MockHelper
|
|||
UserId = 1,
|
||||
ExpiresAt = DateTime.MaxValue,
|
||||
TokenId = 1,
|
||||
UserLocation = "127.0.0.1",
|
||||
UserToken = "unittest",
|
||||
};
|
||||
|
||||
|
|
|
@ -14,23 +14,23 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
|
||||
<PackageReference Include="xunit" Version="2.6.6" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.5.6">
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.10.0" />
|
||||
<PackageReference Include="xunit" Version="2.8.1" />
|
||||
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.1">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.0">
|
||||
<PackageReference Include="coverlet.collector" Version="6.0.2">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Moq" Version="4.20.70" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="8.0.6" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:s="clr-namespace:System;assembly=mscorlib"
|
||||
xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
|
||||
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/Asp/FormatOnClosingTag/@EntryValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/CSharpAnnotationTypingAssist/IsEnabledAfterTypeName/@EntryValue">False</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/CodeEditing/TypingAssist/CSharpAnnotationTypingAssist/IsEnabledAtOtherPositions/@EntryValue">False</s:Boolean>
|
||||
|
@ -96,10 +98,14 @@
|
|||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Method/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue"><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue"><Policy><Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"><ElementKinds><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"><ElementKinds><Kind Name="CONSTANT_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /></Policy></s:String>
|
||||
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=8284009d_002De743_002D4d89_002D9402_002Da5bf9a89b657/@EntryIndexedValue"><Policy><Descriptor Staticness="Any" AccessRightKinds="Any" Description="Methods"><ElementKinds><Kind Name="METHOD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="" Suffix="" Style="AaBb"><ExtraRule Prefix="" Suffix="" Style="aaBb" /></Policy></Policy></s:String>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Affero/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=airfryer/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=ARRRRRRRRRR/@EntryIndexedValue">True</s:Boolean>
|
||||
|
|
|
@ -0,0 +1,6 @@
|
|||
namespace LBPUnion.ProjectLighthouse.Configuration.ConfigurationCategories;
|
||||
|
||||
public class MatchmakingConfiguration
|
||||
{
|
||||
public bool MatchmakingEnabled { get; set; } = true;
|
||||
}
|
|
@ -11,6 +11,12 @@ public class UserGeneratedContentLimitConfiguration
|
|||
|
||||
public int PhotosQuota { get; set; } = 500;
|
||||
|
||||
/// <summary>
|
||||
/// When enabled, all UGC uploads are disabled. This includes levels, photos, reviews,
|
||||
/// comments, and certain profile settings.
|
||||
/// </summary>
|
||||
public bool ReadOnlyMode { get; set; } = false;
|
||||
|
||||
public bool ProfileCommentsEnabled { get; set; } = true;
|
||||
|
||||
public bool LevelCommentsEnabled { get; set; } = true;
|
||||
|
|
|
@ -11,7 +11,7 @@ public class ServerConfiguration : ConfigurationBase<ServerConfiguration>
|
|||
// This is so Lighthouse can properly identify outdated configurations and update them with newer settings accordingly.
|
||||
// If you are modifying anything here, this value MUST be incremented.
|
||||
// Thanks for listening~
|
||||
public override int ConfigVersion { get; set; } = 24;
|
||||
public override int ConfigVersion { get; set; } = 26;
|
||||
|
||||
public override string ConfigName { get; set; } = "lighthouse.yml";
|
||||
public string WebsiteListenUrl { get; set; } = "http://localhost:10060";
|
||||
|
@ -35,6 +35,7 @@ public class ServerConfiguration : ConfigurationBase<ServerConfiguration>
|
|||
public AuthenticationConfiguration Authentication { get; set; } = new();
|
||||
public CaptchaConfiguration Captcha { get; set; } = new();
|
||||
public DigestKeyConfiguration DigestKey { get; set; } = new();
|
||||
public MatchmakingConfiguration Matchmaking { get; set; } = new();
|
||||
public GoogleAnalyticsConfiguration GoogleAnalytics { get; set; } = new();
|
||||
public MailConfiguration Mail { get; set; } = new();
|
||||
public UserGeneratedContentLimitConfiguration UserGeneratedContentLimits { get; set; } = new();
|
||||
|
|
|
@ -67,10 +67,10 @@ public partial class DatabaseContext
|
|||
UserToken = CryptoHelper.GenerateAuthToken(),
|
||||
User = user,
|
||||
UserId = user.UserId,
|
||||
UserLocation = userLocation,
|
||||
GameVersion = npTicket.GameVersion,
|
||||
Platform = npTicket.Platform,
|
||||
TicketHash = npTicket.TicketHash,
|
||||
LocationHash = CryptoHelper.Sha256Hash(userLocation),
|
||||
// we can get away with a low expiry here since LBP will just get a new token everytime it gets 403'd
|
||||
ExpiresAt = DateTime.UtcNow + TimeSpan.FromHours(1),
|
||||
};
|
||||
|
|
|
@ -9,9 +9,6 @@ namespace LBPUnion.ProjectLighthouse.Helpers;
|
|||
|
||||
public static class CryptoHelper
|
||||
{
|
||||
|
||||
private static readonly SHA256 sha256 = SHA256.Create();
|
||||
|
||||
/// <summary>
|
||||
/// Generates a random SHA256 and BCrypted token
|
||||
/// </summary>
|
||||
|
@ -19,10 +16,15 @@ public static class CryptoHelper
|
|||
public static string GenerateAuthToken()
|
||||
{
|
||||
byte[] bytes = (byte[])GenerateRandomBytes(256);
|
||||
|
||||
return BCryptHash(Sha256Hash(bytes));
|
||||
}
|
||||
|
||||
public static string GenerateUrlToken()
|
||||
{
|
||||
byte[] bytes = (byte[])GenerateRandomBytes(256);
|
||||
return Convert.ToBase64String(Encoding.UTF8.GetBytes(BCryptHash(Sha256Hash(bytes))));
|
||||
}
|
||||
|
||||
public static string ComputeDigest(string path, string authCookie, byte[] body, string digestKey, bool excludeBody = false)
|
||||
{
|
||||
|
||||
|
@ -156,7 +158,7 @@ public static class CryptoHelper
|
|||
|
||||
public static string Sha256Hash(string str) => Sha256Hash(Encoding.UTF8.GetBytes(str));
|
||||
|
||||
public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(sha256.ComputeHash(bytes)).Replace("-", "").ToLower();
|
||||
public static string Sha256Hash(byte[] bytes) => BitConverter.ToString(SHA256.HashData(bytes)).Replace("-", "").ToLower();
|
||||
|
||||
public static string Sha1Hash(byte[] bytes) => BitConverter.ToString(SHA1.HashData(bytes)).Replace("-", "");
|
||||
|
||||
|
|
|
@ -52,7 +52,7 @@ public static class SMTPHelper
|
|||
{
|
||||
Created = DateTime.UtcNow,
|
||||
UserId = user.UserId,
|
||||
ResetToken = CryptoHelper.GenerateAuthToken(),
|
||||
ResetToken = CryptoHelper.GenerateUrlToken(),
|
||||
};
|
||||
|
||||
database.PasswordResetTokens.Add(token);
|
||||
|
@ -92,7 +92,7 @@ public static class SMTPHelper
|
|||
{
|
||||
UserId = user.UserId,
|
||||
User = user,
|
||||
EmailToken = CryptoHelper.GenerateAuthToken(),
|
||||
EmailToken = CryptoHelper.GenerateUrlToken(),
|
||||
ExpiresAt = DateTime.UtcNow.AddHours(6),
|
||||
};
|
||||
|
||||
|
|
|
@ -11,15 +11,8 @@ namespace LBPUnion.ProjectLighthouse.Helpers;
|
|||
|
||||
public static partial class MatchHelper
|
||||
{
|
||||
public static readonly ConcurrentDictionary<int, string?> UserLocations = new();
|
||||
public static readonly ConcurrentDictionary<int, List<int>?> UserRecentlyDivedIn = new();
|
||||
|
||||
public static void SetUserLocation(int userId, string location)
|
||||
{
|
||||
if (UserLocations.TryGetValue(userId, out string? _)) UserLocations.TryRemove(userId, out _);
|
||||
UserLocations.TryAdd(userId, location);
|
||||
}
|
||||
|
||||
public static void AddUserRecentlyDivedIn(int userId, int otherUserId)
|
||||
{
|
||||
if (!UserRecentlyDivedIn.TryGetValue(userId, out List<int>? recentlyDivedIn)) UserRecentlyDivedIn.TryAdd(userId, recentlyDivedIn = new List<int>());
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -16,6 +15,9 @@ using LBPUnion.ProjectLighthouse.Types.Logging;
|
|||
using LBPUnion.ProjectLighthouse.Types.Matchmaking;
|
||||
using LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms;
|
||||
using LBPUnion.ProjectLighthouse.Types.Users;
|
||||
#if DEBUG
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Helpers;
|
||||
|
||||
|
@ -27,7 +29,7 @@ public static class RoomHelper
|
|||
private static int roomIdIncrement;
|
||||
internal static int RoomIdIncrement => roomIdIncrement++;
|
||||
|
||||
public static FindBestRoomResponse? FindBestRoom(DatabaseContext database, UserEntity? user, GameVersion roomVersion, RoomSlot? slot, Platform? platform, string? location)
|
||||
public static FindBestRoomResponse? FindBestRoom(DatabaseContext database, UserEntity? user, GameVersion roomVersion, RoomSlot? slot, Platform? platform)
|
||||
{
|
||||
if (roomVersion == GameVersion.LittleBigPlanet1 || roomVersion == GameVersion.LittleBigPlanetPSP)
|
||||
{
|
||||
|
@ -61,30 +63,10 @@ public static class RoomHelper
|
|||
{
|
||||
if (user != null && MatchHelper.DidUserRecentlyDiveInWith(user.UserId, room.HostId)) continue;
|
||||
|
||||
Dictionary<int, string> relevantUserLocations = new();
|
||||
|
||||
// Determine if all players in a room have UserLocations stored, also store the relevant userlocations while we're at it
|
||||
bool allPlayersHaveLocations = room.PlayerIds.All
|
||||
(
|
||||
p =>
|
||||
{
|
||||
bool gotValue = MatchHelper.UserLocations.TryGetValue(p, out string? value);
|
||||
|
||||
if (gotValue && value != null) relevantUserLocations.TryAdd(p, value);
|
||||
return gotValue;
|
||||
}
|
||||
);
|
||||
|
||||
// If we don't have all locations then the game won't know how to communicate. Thus, it's not a valid room.
|
||||
if (!allPlayersHaveLocations) continue;
|
||||
|
||||
// If we got here then it should be a valid room.
|
||||
|
||||
FindBestRoomResponse response = new()
|
||||
{
|
||||
RoomId = room.RoomId,
|
||||
Players = new List<Player>(),
|
||||
Locations = new List<string>(),
|
||||
};
|
||||
|
||||
foreach (UserEntity player in room.GetPlayers(database))
|
||||
|
@ -97,8 +79,6 @@ public static class RoomHelper
|
|||
User = player,
|
||||
}
|
||||
);
|
||||
|
||||
response.Locations.Add(relevantUserLocations.GetValueOrDefault(player.UserId)); // Already validated to exist
|
||||
}
|
||||
|
||||
if (user != null)
|
||||
|
@ -111,8 +91,6 @@ public static class RoomHelper
|
|||
}
|
||||
);
|
||||
|
||||
if (location == null) response.Locations.Add(location);
|
||||
|
||||
response.Slots = new List<List<int>>
|
||||
{
|
||||
new()
|
||||
|
@ -189,7 +167,7 @@ public static class RoomHelper
|
|||
{
|
||||
StorableList<Room> rooms = Rooms; // cache rooms so we dont gen a new one every time
|
||||
List<Room> roomsToUpdate = new();
|
||||
|
||||
|
||||
#if DEBUG
|
||||
Logger.Debug($"Cleaning up rooms... (took {stopwatch.ElapsedMilliseconds}ms to get lock on {nameof(RoomLock)})", LogArea.Match);
|
||||
#endif
|
||||
|
@ -205,7 +183,7 @@ public static class RoomHelper
|
|||
.ToList();
|
||||
|
||||
foreach (int player in playersToRemove) room.PlayerIds.Remove(player);
|
||||
|
||||
|
||||
roomsToUpdate.Add(room);
|
||||
}
|
||||
|
||||
|
@ -248,7 +226,7 @@ public static class RoomHelper
|
|||
int roomCountAfterCleanup = rooms.Count();
|
||||
|
||||
// Log the amount of rooms cleaned up.
|
||||
// If we didnt clean any rooms, it's not useful to log in a
|
||||
// If we didnt clean any rooms, it's not useful to log in a
|
||||
// production environment but it's still quite useful for debugging.
|
||||
//
|
||||
// So, we handle that case here:
|
||||
|
|
|
@ -19,7 +19,6 @@ public static class VersionHelper
|
|||
public static string EnvVer => $"{ServerConfiguration.Instance.Customization.EnvironmentName} {FullRevision}";
|
||||
public static string FullVersion =>
|
||||
$"Project Lighthouse {ServerConfiguration.Instance.Customization.EnvironmentName} {Branch}@{CommitHash} {Build}";
|
||||
public static bool IsDirty => ThisAssembly.Git.IsDirty;
|
||||
public static string RepositoryUrl => ThisAssembly.Git.RepositoryUrl;
|
||||
|
||||
public const string Build =
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20240317231145_RemoveUserLocationFromGameTokens")]
|
||||
public partial class RemoveUserLocationFromGameTokens : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "UserLocation",
|
||||
table: "GameTokens");
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "UserLocation",
|
||||
table: "GameTokens",
|
||||
type: "longtext",
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,598 @@
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Migrations
|
||||
{
|
||||
[DbContext(typeof(DatabaseContext))]
|
||||
[Migration("20240625004457_AddLocationHashToGameToken")]
|
||||
public partial class AddLocationHashToGameToken : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "WebTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "AnnouncementId",
|
||||
table: "WebsiteAnnouncements",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "VisitedLevelId",
|
||||
table: "VisitedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "UserId",
|
||||
table: "Users",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "SlotId",
|
||||
table: "Slots",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ScoreId",
|
||||
table: "Scores",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ReviewId",
|
||||
table: "Reviews",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ReportId",
|
||||
table: "Reports",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "RegistrationTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "RatedReviewId",
|
||||
table: "RatedReviews",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "RatedLevelId",
|
||||
table: "RatedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "RatingId",
|
||||
table: "RatedComments",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "QueuedLevelId",
|
||||
table: "QueuedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PlaylistId",
|
||||
table: "Playlists",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PlatformLinkAttemptId",
|
||||
table: "PlatformLinkAttempts",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PhotoSubjectId",
|
||||
table: "PhotoSubjects",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PhotoId",
|
||||
table: "Photos",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "PasswordResetTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Id",
|
||||
table: "Notifications",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "HeartedProfileId",
|
||||
table: "HeartedProfiles",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "HeartedPlaylistId",
|
||||
table: "HeartedPlaylists",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "HeartedLevelId",
|
||||
table: "HeartedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "TicketHash",
|
||||
table: "GameTokens",
|
||||
type: "varchar(64)",
|
||||
maxLength: 64,
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "longtext",
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "GameTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AddColumn<string>(
|
||||
name: "LocationHash",
|
||||
table: "GameTokens",
|
||||
type: "varchar(64)",
|
||||
maxLength: 64,
|
||||
nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EmailVerificationTokenId",
|
||||
table: "EmailVerificationTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EmailSetTokenId",
|
||||
table: "EmailSetTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CategoryId",
|
||||
table: "CustomCategories",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CommentId",
|
||||
table: "Comments",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CaseId",
|
||||
table: "Cases",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "BlockedProfileId",
|
||||
table: "BlockedProfiles",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Id",
|
||||
table: "APIKeys",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropColumn(
|
||||
name: "LocationHash",
|
||||
table: "GameTokens");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "WebTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "AnnouncementId",
|
||||
table: "WebsiteAnnouncements",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "VisitedLevelId",
|
||||
table: "VisitedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "UserId",
|
||||
table: "Users",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "SlotId",
|
||||
table: "Slots",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ScoreId",
|
||||
table: "Scores",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ReviewId",
|
||||
table: "Reviews",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "ReportId",
|
||||
table: "Reports",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "RegistrationTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "RatedReviewId",
|
||||
table: "RatedReviews",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "RatedLevelId",
|
||||
table: "RatedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "RatingId",
|
||||
table: "RatedComments",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "QueuedLevelId",
|
||||
table: "QueuedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PlaylistId",
|
||||
table: "Playlists",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PlatformLinkAttemptId",
|
||||
table: "PlatformLinkAttempts",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PhotoSubjectId",
|
||||
table: "PhotoSubjects",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "PhotoId",
|
||||
table: "Photos",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "PasswordResetTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Id",
|
||||
table: "Notifications",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "HeartedProfileId",
|
||||
table: "HeartedProfiles",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "HeartedPlaylistId",
|
||||
table: "HeartedPlaylists",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "HeartedLevelId",
|
||||
table: "HeartedLevels",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<string>(
|
||||
name: "TicketHash",
|
||||
table: "GameTokens",
|
||||
type: "longtext",
|
||||
nullable: true,
|
||||
oldClrType: typeof(string),
|
||||
oldType: "varchar(64)",
|
||||
oldMaxLength: 64,
|
||||
oldNullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4")
|
||||
.OldAnnotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "TokenId",
|
||||
table: "GameTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EmailVerificationTokenId",
|
||||
table: "EmailVerificationTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "EmailSetTokenId",
|
||||
table: "EmailSetTokens",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CategoryId",
|
||||
table: "CustomCategories",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CommentId",
|
||||
table: "Comments",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "CaseId",
|
||||
table: "Cases",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "BlockedProfileId",
|
||||
table: "BlockedProfiles",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
migrationBuilder.AlterColumn<int>(
|
||||
name: "Id",
|
||||
table: "APIKeys",
|
||||
type: "int",
|
||||
nullable: false,
|
||||
oldClrType: typeof(int),
|
||||
oldType: "int")
|
||||
.OldAnnotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,6 +3,7 @@ using System;
|
|||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
|
||||
#nullable disable
|
||||
|
@ -16,15 +17,19 @@ namespace ProjectLighthouse.Migrations
|
|||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "7.0.13")
|
||||
.HasAnnotation("ProductVersion", "8.0.6")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 64);
|
||||
|
||||
MySqlModelBuilderExtensions.AutoIncrementColumns(modelBuilder);
|
||||
|
||||
modelBuilder.Entity("LBPUnion.ProjectLighthouse.Types.Entities.Interaction.HeartedLevelEntity", b =>
|
||||
{
|
||||
b.Property<int>("HeartedLevelId")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("HeartedLevelId"));
|
||||
|
||||
b.Property<int>("SlotId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -46,6 +51,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("HeartedPlaylistId"));
|
||||
|
||||
b.Property<int>("PlaylistId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -67,6 +74,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("HeartedProfileId"));
|
||||
|
||||
b.Property<int>("HeartedUserId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -88,6 +97,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("QueuedLevelId"));
|
||||
|
||||
b.Property<int>("SlotId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -109,6 +120,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("RatingId"));
|
||||
|
||||
b.Property<int>("CommentId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -133,6 +146,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("RatedLevelId"));
|
||||
|
||||
b.Property<int>("Rating")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -163,6 +178,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("RatedReviewId"));
|
||||
|
||||
b.Property<int>("ReviewId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -187,6 +204,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("VisitedLevelId"));
|
||||
|
||||
b.Property<int>("PlaysLBP1")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -217,6 +236,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("CategoryId"));
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -243,6 +264,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("PlaylistId"));
|
||||
|
||||
b.Property<int>("CreatorId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -271,6 +294,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("ReviewId"));
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
|
@ -318,6 +343,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("ScoreId"));
|
||||
|
||||
b.Property<int>("ChildSlotId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -351,6 +378,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("SlotId"));
|
||||
|
||||
b.Property<string>("AuthorLabels")
|
||||
.IsRequired()
|
||||
.HasColumnType("longtext");
|
||||
|
@ -504,6 +533,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("ReportId"));
|
||||
|
||||
b.Property<string>("Bounds")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -550,6 +581,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("CaseId"));
|
||||
|
||||
b.Property<int>("AffectedId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -604,6 +637,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<bool>("IsDismissed")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
|
@ -629,6 +664,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("BlockedProfileId"));
|
||||
|
||||
b.Property<int>("BlockedUserId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -650,6 +687,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("CommentId"));
|
||||
|
||||
b.Property<bool>("Deleted")
|
||||
.HasColumnType("tinyint(1)");
|
||||
|
||||
|
@ -719,6 +758,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("PhotoId"));
|
||||
|
||||
b.Property<int>("CreatorId")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -759,6 +800,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("PhotoSubjectId"));
|
||||
|
||||
b.Property<string>("Bounds")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -783,6 +826,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("PlatformLinkAttemptId"));
|
||||
|
||||
b.Property<string>("IPAddress")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -811,6 +856,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("UserId"));
|
||||
|
||||
b.Property<int>("AdminGrantedSlots")
|
||||
.HasColumnType("int");
|
||||
|
||||
|
@ -916,6 +963,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("Id"));
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
|
@ -936,6 +985,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("EmailSetTokenId"));
|
||||
|
||||
b.Property<string>("EmailToken")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -958,6 +1009,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("EmailVerificationTokenId"));
|
||||
|
||||
b.Property<string>("EmailToken")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -980,24 +1033,28 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("TokenId"));
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
b.Property<int>("GameVersion")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("LocationHash")
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<int>("Platform")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("TicketHash")
|
||||
.HasColumnType("longtext");
|
||||
.HasMaxLength(64)
|
||||
.HasColumnType("varchar(64)");
|
||||
|
||||
b.Property<int>("UserId")
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("UserLocation")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
b.Property<string>("UserToken")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
@ -1014,6 +1071,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("TokenId"));
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
|
@ -1034,6 +1093,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("TokenId"));
|
||||
|
||||
b.Property<DateTime>("Created")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
|
@ -1054,6 +1115,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("TokenId"));
|
||||
|
||||
b.Property<DateTime>("ExpiresAt")
|
||||
.HasColumnType("datetime(6)");
|
||||
|
||||
|
@ -1077,6 +1140,8 @@ namespace ProjectLighthouse.Migrations
|
|||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
MySqlPropertyBuilderExtensions.UseMySqlIdentityColumn(b.Property<int>("AnnouncementId"));
|
||||
|
||||
b.Property<string>("Content")
|
||||
.HasColumnType("longtext");
|
||||
|
||||
|
|
|
@ -10,31 +10,31 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BCrypt.Net-Next" Version="4.0.3" />
|
||||
<PackageReference Include="GitInfo" Version="3.3.3">
|
||||
<PackageReference Include="GitInfo" Version="3.3.5">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pfim" Version="0.11.2" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.2" />
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.13.0" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.4" />
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.15.0" />
|
||||
<PackageReference Include="JetBrains.Annotations" Version="2023.3.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.2" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.2">
|
||||
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="8.0.6" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="8.0.6">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.0" />
|
||||
<PackageReference Include="Redis.OM" Version="0.6.1" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="Redis.OM" Version="0.7.1" />
|
||||
<PackageReference Include="SharpZipLib" Version="1.4.2" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />
|
||||
<PackageReference Include="YamlDotNet" Version="15.1.0" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.2.1" />
|
||||
<PackageReference Include="DistributedLock.MySql" Version="1.0.1" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.6.2" />
|
||||
<PackageReference Include="YamlDotNet" Version="15.1.6" />
|
||||
<PackageReference Include="BouncyCastle.Cryptography" Version="2.4.0" />
|
||||
<PackageReference Include="DistributedLock.MySql" Version="1.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\ProjectLighthouse.Localization\ProjectLighthouse.Localization.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -53,12 +53,6 @@ public static class StartupTasks
|
|||
|
||||
// Version info depends on ServerConfig
|
||||
Logger.Info($"You are running version {VersionHelper.FullVersion}", LogArea.Startup);
|
||||
if (VersionHelper.IsDirty)
|
||||
{
|
||||
Logger.Warn("This is a modified version of Project Lighthouse. " +
|
||||
"Please make sure you are properly disclosing the source code to any users who may be using this instance.",
|
||||
LogArea.Startup);
|
||||
}
|
||||
|
||||
Logger.Info("Connecting to the database...", LogArea.Startup);
|
||||
|
||||
|
|
|
@ -185,7 +185,7 @@ public class NPTicket
|
|||
}
|
||||
|
||||
// Used to identify duplicate tickets
|
||||
npTicket.TicketHash = CryptoHelper.Sha1Hash(data);
|
||||
npTicket.TicketHash = CryptoHelper.Sha256Hash(data);
|
||||
|
||||
#if DEBUG
|
||||
Logger.Debug("npTicket data:", LogArea.Login);
|
||||
|
|
|
@ -33,6 +33,9 @@ public struct SectionHeader
|
|||
public SectionType Type;
|
||||
public ushort Length;
|
||||
public int Position;
|
||||
|
||||
public override string ToString() =>
|
||||
$"SectionHeader(Type='{this.Type}', Length='{this.Length}', Position='{this.Position}')";
|
||||
}
|
||||
|
||||
public class TicketReader : BinaryReader
|
||||
|
|
|
@ -19,13 +19,15 @@ public class GameTokenEntity
|
|||
|
||||
public string UserToken { get; set; }
|
||||
|
||||
public string UserLocation { get; set; }
|
||||
|
||||
public GameVersion GameVersion { get; set; }
|
||||
|
||||
public Platform Platform { get; set; }
|
||||
|
||||
[StringLength(64)]
|
||||
public string TicketHash { get; set; }
|
||||
|
||||
[StringLength(64)]
|
||||
public string LocationHash { get; set; }
|
||||
|
||||
public DateTime ExpiresAt { get; set; }
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace LBPUnion.ProjectLighthouse.Types.Matchmaking.Rooms;
|
||||
|
@ -14,8 +13,4 @@ public class FindBestRoomResponse
|
|||
|
||||
[JsonIgnore]
|
||||
public IEnumerable<int> FirstSlot => this.Slots[0];
|
||||
|
||||
[JsonPropertyName("Location")]
|
||||
[SuppressMessage("ReSharper", "CollectionNeverQueried.Global")]
|
||||
public List<string> Locations { get; set; }
|
||||
}
|
|
@ -1,6 +1,8 @@
|
|||
using System.Linq;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Serialization;
|
||||
using LBPUnion.ProjectLighthouse.Configuration;
|
||||
using LBPUnion.ProjectLighthouse.Database;
|
||||
using LBPUnion.ProjectLighthouse.Helpers;
|
||||
using LBPUnion.ProjectLighthouse.Types.Entities.Profile;
|
||||
|
@ -30,6 +32,13 @@ public class GameDeveloperSlot : SlotBase, INeedsPreparationForSerialization
|
|||
[XmlElement("photoCount")]
|
||||
public int PhotoCount { get; set; }
|
||||
|
||||
[XmlElement("commentsEnabled")]
|
||||
public bool CommentsEnabled
|
||||
{
|
||||
get => ServerConfiguration.Instance.UserGeneratedContentLimits.LevelCommentsEnabled;
|
||||
set => throw new NotSupportedException();
|
||||
}
|
||||
|
||||
public async Task PrepareSerialization(DatabaseContext database)
|
||||
{
|
||||
if (this.SlotId == 0 || this.InternalSlotId == 0) return;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue