Bugfixes. Optimizations/refactor. Add redis for player-cache. Add docker files. Replace sqlite dep. Single-Calc for existing players. Game-Metrics in JSON.

This commit is contained in:
MaxJa4
2024-01-21 00:49:20 +01:00
parent 069d76520e
commit da1108d441
41 changed files with 1154 additions and 203 deletions

6
.dockerignore Normal file
View File

@@ -0,0 +1,6 @@
.db
.log
redis
Dockerfile
docker-compose.yml
.gitignore

1
.gitignore vendored
View File

@@ -1,2 +1,3 @@
/isc_rest.log /isc_rest.log
/isc_data.db /isc_data.db
redis

427
.idea/inspectionProfiles/Go_Error.xml generated Normal file
View File

@@ -0,0 +1,427 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0">
<option name="myName" value="Go-Error" />
<inspection_tool class="Annotator" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="BadExpressionStatementJS" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CallerJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckDtdRefs" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckImageSize" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckTagEmptyBody" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CheckValidXmlInScriptTagBody" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CheckXmlFileWithXercesValidator" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CommaExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ComposeMissingKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ComposeUnknownKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ComposeUnknownValues" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ComposeUnquotedPorts" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ConstantConditionalExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ContinueOrBreakFromFinallyBlockJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssDeprecatedValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidAtRule" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidCharsetRule" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidCustomPropertyAtRuleDeclaration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidCustomPropertyAtRuleName" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidFunction" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidHtmlTagReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssInvalidMediaFeature" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPropertyValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssInvalidPseudoSelector" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssMissingComma" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssNegativeValue" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssNoGenericFontName" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssNonIntegerLengthInPixels" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssOverwrittenProperties" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssRedundantUnit" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CssReplaceWithShorthandSafely" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="CssReplaceWithShorthandUnsafely" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="CssUnknownProperty" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myCustomPropertiesEnabled" value="false" />
<option name="myIgnoreVendorSpecificProperties" value="false" />
<option name="myCustomPropertiesList">
<value>
<list size="0" />
</value>
</option>
</inspection_tool>
<inspection_tool class="CssUnknownTarget" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnknownUnit" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedClassInComposesRule" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnresolvedCustomProperty" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="CssUnusedSymbol" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="CustomRegExpInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DockerFileAddOrCopyPaths" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DockerFileArgumentCount" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DockerFileAssignments" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DockerFileRunCommandMissingContinuation" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="DockerJsonFormStringLiterals" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="DuplicatedCode" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6BindWithArrowFunction" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ClassMemberInitializationOrder" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ConvertIndexedForToForOf" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertLetToConst" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertModuleExportToExport" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertRequireIntoImport" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertToForOf" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6ConvertVarToLetConst" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6DestructuringVariablesMerge" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6MissingAwait" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6PossiblyAsyncFunction" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6PreferShortImport" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ES6RedundantAwait" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6RedundantNestingInTemplateLiteral" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ES6ShorthandObjectProperty" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="ES6UnusedImports" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigCharClassLetterRedundancy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigCharClassRedundancy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigDeprecatedDescriptor" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigEmptyHeader" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigEmptySection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigEncoding" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigHeaderUniqueness" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigKeyCorrectness" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigListAcceptability" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigMissingRequiredDeclaration" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigNoMatchingFiles" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigNumerousWildcards" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigOptionRedundancy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigPairAcceptability" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigPartialOverride" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigPatternEnumerationRedundancy" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigPatternRedundancy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigReferenceCorrectness" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigRootDeclarationCorrectness" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigRootDeclarationUniqueness" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigShadowedOption" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigShadowingOption" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigSpaceInHeader" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigUnexpectedComma" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigUnusedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EditorConfigValueCorrectness" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigValueUniqueness" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigVerifyByCore" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="EditorConfigWildcardRedundancy" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="EmptyStatementBodyJS" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_reportEmptyBlocks" value="false" />
</inspection_tool>
<inspection_tool class="ExceptionCaughtLocallyJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FallThroughInSwitchStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FlowJSConfig" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="FlowJSFlagCommentPlacement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="GoContextTodo" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="GoCoverageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="GoDfaConstantCondition" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GoDfaErrorMayBeNotNil" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GoDfaInspectionRunner" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="GoDfaNilDereference" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="GoMissingTrailingComma" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="GoUnnecessarilyExportedIdentifiers" enabled="true" level="WEAK WARNING" enabled_by_default="true" />
<inspection_tool class="GrazieInspection" enabled="false" level="GRAMMAR_ERROR" enabled_by_default="false" />
<inspection_tool class="GrpcSchemes" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlDeprecatedAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlDeprecatedTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlExtraClosingTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlMissingClosingTag" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="HtmlRequiredAltAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlRequiredLangAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlRequiredTitleElement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAnchorTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAttribute" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="7">
<item index="0" class="java.lang.String" itemvalue="hx-get" />
<item index="1" class="java.lang.String" itemvalue="hx-target" />
<item index="2" class="java.lang.String" itemvalue="hx-vals" />
<item index="3" class="java.lang.String" itemvalue="hx-post" />
<item index="4" class="java.lang.String" itemvalue="hx-trigger" />
<item index="5" class="java.lang.String" itemvalue="hx-swap" />
<item index="6" class="java.lang.String" itemvalue="hx-delete" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownBooleanAttribute" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownTag" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myValues">
<value>
<list size="6">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownTarget" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlWrongAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HttpClientUnresolvedAuthId" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="HttpClientUnresolvedVariable" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HttpRequestContentLengthIsIgnored" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HttpRequestPlaceholder" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="HttpRequestWhitespaceInsideRequestTargetPath" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="HttpUrlsUsage" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="IgnoreFileDuplicateEntry" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="IncompatibleMaskJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="IncorrectHttpHeaderInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InfiniteLoopJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InfiniteRecursionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="InjectedReferences" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSAccessibilityCheck" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSAnnotator" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSArrowFunctionBracesCanBeRemoved" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSAssignmentUsedAsCondition" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSBitwiseOperatorUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSCheckFunctionSignatures" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSClosureCompilerSyntax" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSCommentMatchesSignature" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSComparisonWithNaN" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSConsecutiveCommasInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSConstantReassignment" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSDeprecatedSymbols" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSDuplicateCaseLabel" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSDuplicatedDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSEqualityComparisonWithCoercion" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSFileReferences" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSFunctionExpressionToArrowFunction" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSIgnoredPromiseFromCall" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSIncompatibleTypesComparison" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSJQueryEfficiency" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSJoinVariableDeclarationAndAssignment" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSLastCommaInObjectLiteral" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSMethodCanBeStatic" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSMismatchedCollectionQueryUpdate" enabled="false" level="WARNING" enabled_by_default="false">
<option name="queries" value="trace,write,forEach,length,size" />
<option name="updates" value="pop,push,shift,splice,unshift,add,insert,remove,reverse,copyWithin,fill,sort" />
</inspection_tool>
<inspection_tool class="JSMissingSwitchBranches" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSMissingSwitchDefault" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSNonASCIINames" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSObjectNullOrUndefined" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSOctalInteger" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JSPotentiallyInvalidConstructorUsage" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myConsiderUppercaseFunctionsToBeConstructors" value="true" />
</inspection_tool>
<inspection_tool class="JSPotentiallyInvalidTargetOfIndexedPropertyAccess" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPotentiallyInvalidUsageOfClassThis" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPotentiallyInvalidUsageOfThis" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSPrimitiveTypeWrapperUsage" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSRedundantSwitchStatement" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSReferencingMutableVariableFromClosure" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSRemoveUnnecessaryParentheses" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSStringConcatenationToES6Template" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSSuspiciousEqPlus" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSSuspiciousNameCombination" enabled="false" level="WARNING" enabled_by_default="false">
<group names="x,width,left,right" />
<group names="y,height,top,bottom" />
<exclude classes="Math" />
</inspection_tool>
<inspection_tool class="JSSwitchVariableDeclarationIssue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSTestFailedLine" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSTypeOfValues" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUndeclaredVariable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUndefinedPropertyAssignment" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnnecessarySemicolon" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnreachableSwitchBranches" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedExtXType" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedLibraryURL" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnresolvedReference" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedGlobalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUnusedLocalSymbols" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSUrlImportUsage" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSValidateJSDoc" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSValidateTypes" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JSVoidFunctionReturnValueUsed" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSXDomNesting" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JSXNamespaceValidation" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="JSXUnresolvedComponent" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="Json5StandardCompliance" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="JsonDuplicatePropertyKeys" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JsonSchemaCompliance" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JsonSchemaDeprecation" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="JsonSchemaRefReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="JsonStandardCompliance" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="LanguageDetectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LoopStatementThatDoesntLoopJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="LossyEncoding" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MakefileUnresolvedPrerequisite" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownIncorrectTableFormatting" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownIncorrectlyNumberedListItem" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownLinkDestinationWithSpaces" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownNoTableBorders" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownOutdatedTableOfContents" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownUnresolvedFileReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownUnresolvedHeaderReference" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MarkdownUnresolvedLinkLabel" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MongoJSDeprecationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MongoJSExtDeprecationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MongoJSExtResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MongoJSExtSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MongoJSResolveInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MongoJSSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MsBuiltinInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MsOrderByInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="MysqlLoadDataPathInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MysqlParsingInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="MysqlSpaceAfterFunctionNameInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="NodeCoreCodingAssistance" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NonAsciiCharacters" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="NpmUsedModulesInstalled" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="NpmVulnerableApiCode" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OraMissingBodyInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OraOverloadInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="OraUnmatchedForwardDeclarationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="PackageJsonMismatchedDependency" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PbDuplicatedImports" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PgSelectFromProcedureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PointlessArithmeticExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="PointlessBooleanExpressionJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ReassignedToPlainText" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RedundantSuppression" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpDuplicateAlternationBranch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpDuplicateCharacterInClass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpEmptyAlternationBranch" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpEscapedMetaCharacter" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="RegExpOctalEscape" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="RegExpRedundantClassElement" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpRedundantEscape" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpRedundantNestedCharacterClass" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpRepeatedSpace" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpSimplifiable" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpSingleCharAlternation" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpSuspiciousBackref" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpUnexpectedAnchor" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RegExpUnnecessaryNonCapturingGroup" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="RequiredAttributes" enabled="false" level="WARNING" enabled_by_default="false">
<option name="myAdditionalRequiredHtmlAttributes" value="" />
</inspection_tool>
<inspection_tool class="ReservedWordUsedAsNameJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ReturnFromFinallyBlockJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SSBasedInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ShellCheck" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="ShiftOutOfRangeJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SillyAssignmentJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
<option name="processCode" value="true" />
<option name="processLiterals" value="true" />
<option name="processComments" value="true" />
</inspection_tool>
<inspection_tool class="SqlAddNotNullColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAggregatesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAmbiguousColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlAutoIncrementDuplicateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCallNotationInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SqlCaseVsCoalesceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCaseVsIfInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCheckUsingColumnsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantConditionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlConstantExpressionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlCurrentSchemaInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDeprecateTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDerivedTableAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDialectInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDropIndexedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDtInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlDuplicateColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIdentifierInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlIllegalCursorStateInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertIntoGeneratedColumnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertNullIntoNotNullInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlInsertValuesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlJoinWithoutOnInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlMisleadingReferenceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlMissingReturnInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SqlMultipleLimitClausesInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNoDataSourceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlNullComparisonInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlRedundantAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlRedundantCodeInCoalesceInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlRedundantElseNullInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlRedundantLimitInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlRedundantOrderingDirectionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlResolveInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SqlShadowingAliasInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlShouldBeInGroupByInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSideEffectsInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlSignatureInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStorageInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlStringLengthExceededInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTransactionStatementInTriggerInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTriggerTransitionInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlTypeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnicodeStringLiteralInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnreachableCodeInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedCteInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedSubqueryItemInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlUnusedVariableInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SqlWithoutWhereInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SuspiciousTypeOfGuard" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SwJsonMaybeSpecificationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SwJsonUnresolvedReferencesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="SwYamlMaybeSpecificationInspection" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="SwYamlUnresolvedReferencesInspection" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TaskProblemsInspection" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="ThisExpressionReferencesGlobalObjectJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ThrowFromFinallyBlockJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialConditionalJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TrivialIfJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptAbstractClassConstructorCanBeMadeProtected" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptCheckImport" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypeScriptConfig" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptDuplicateUnionOrIntersectionType" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptExplicitMemberType" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="TypeScriptFieldCanBeMadeReadonly" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptJSXUnresolvedComponent" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptLibrary" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypeScriptMissingAugmentationImport" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="TypeScriptMissingConfigOption" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptRedundantGenericType" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptSmartCast" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptSuspiciousConstructorParameterAssignment" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptUMDGlobal" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptUnresolvedReference" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypeScriptValidateGenericTypes" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="TypeScriptValidateJSTypes" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="TypeScriptValidateTypes" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="UnnecessaryContinueJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLabelJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLabelOnBreakStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLabelOnContinueStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnnecessaryLocalVariableJS" enabled="false" level="WARNING" enabled_by_default="false">
<option name="m_ignoreImmediatelyReturnedVariables" value="false" />
<option name="m_ignoreAnnotatedVariables" value="false" />
</inspection_tool>
<inspection_tool class="UnnecessaryReturnJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnreachableCodeJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="UnresolvedReference" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="UpdateDependencyToLatestVersion" enabled="false" level="INFORMATION" enabled_by_default="false" />
<inspection_tool class="VulnerableLibrariesLocal" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="WebpackConfigHighlighting" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="WithStatementJS" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDefaultAttributeValue" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDeprecatedElement" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlDuplicatedId" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlHighlighting" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlInvalidId" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlPathReference" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="XmlUnboundNsPrefix" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlUnusedNamespaceDeclaration" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="XmlWrongRootElement" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLDuplicatedKeys" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLIncompatibleTypes" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLRecursiveAlias" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLSchemaDeprecation" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLSchemaValidation" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="YAMLUnresolvedAlias" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="YAMLUnusedAnchor" enabled="false" level="WARNING" enabled_by_default="false" />
</profile>
</component>

View File

@@ -3,6 +3,7 @@
<deployment type="docker-image"> <deployment type="docker-image">
<settings> <settings>
<option name="imageTag" value="redis:alpine" /> <option name="imageTag" value="redis:alpine" />
<option name="command" value="redis-server --save 5 1 --loglevel warning" />
<option name="containerName" value="redis-isc" /> <option name="containerName" value="redis-isc" />
<option name="portBindings"> <option name="portBindings">
<list> <list>
@@ -12,6 +13,14 @@
</DockerPortBindingImpl> </DockerPortBindingImpl>
</list> </list>
</option> </option>
<option name="volumeBindings">
<list>
<DockerVolumeBindingImpl>
<option name="containerPath" value="/data" />
<option name="hostPath" value="E:\GO\InfantrySkillCalculator\redis" />
</DockerVolumeBindingImpl>
</list>
</option>
</settings> </settings>
</deployment> </deployment>
<method v="2" /> <method v="2" />

24
Dockerfile Normal file
View File

@@ -0,0 +1,24 @@
FROM golang:1.21-alpine AS builder
WORKDIR /app
COPY go.mod go.sum internal/**/go.mod ./
RUN go mod download
COPY . .
RUN CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags="-w -s" -o isc .
FROM alpine:latest
WORKDIR /root/
COPY --from=builder /app/isc .
COPY --from=builder /app/templates ./templates
COPY --from=builder /app/static ./static
COPY --from=builder /app/config ./config
EXPOSE 8000
CMD ["./isc"]

27
auth.go
View File

@@ -3,12 +3,12 @@ package main
import ( import (
"InfantrySkillCalculator/controllers" "InfantrySkillCalculator/controllers"
"InfantrySkillCalculator/models" "InfantrySkillCalculator/models"
"InfantrySkillCalculator/utils"
"errors" "errors"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"golang.org/x/crypto/bcrypt" "golang.org/x/crypto/bcrypt"
"gorm.io/gorm" "gorm.io/gorm"
"internal/session"
"log" "log"
"net/http" "net/http"
) )
@@ -48,25 +48,24 @@ func hashPassword(password string) (string, error) {
func AuthRequired() gin.HandlerFunc { func AuthRequired() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) auth, okAuth := session.GetAuthenticated(c)
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth || !controllers.IsUserEnabled(session.Values["username"].(string)) { username, okUser := session.GetUsername(c)
if !okAuth || !okUser || !auth || !controllers.IsUserEnabled(username) {
redirectToLogin(c) redirectToLogin(c)
return return
} }
c.Next() c.Next()
} }
} }
func AdminAuthRequired() gin.HandlerFunc { func AdminAuthRequired() gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) auth, okAuth := session.GetAuthenticated(c)
if auth, ok := session.Values["authenticated"].(bool); !ok || !auth { username, okUser := session.GetUsername(c)
redirectToLogin(c)
return
}
username, ok := session.Values["username"].(string) if !okAuth || !okUser || !auth || !controllers.IsUserEnabled(username) || !controllers.IsUserAdmin(username) {
if !ok || !controllers.IsUserEnabled(username) || !controllers.IsUserAdmin(username) {
redirectToLogin(c) redirectToLogin(c)
return return
} }
@@ -76,8 +75,7 @@ func AdminAuthRequired() gin.HandlerFunc {
} }
func isUserAdmin(c *gin.Context) bool { func isUserAdmin(c *gin.Context) bool {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) username, ok := session.GetUsername(c)
username, ok := session.Values["username"].(string)
if !ok { if !ok {
return false return false
} }
@@ -85,10 +83,7 @@ func isUserAdmin(c *gin.Context) bool {
} }
func redirectToLogin(c *gin.Context) { func redirectToLogin(c *gin.Context) {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) if err := session.InvalidateSession(c); err != nil {
session.Options.MaxAge = -1
err := session.Save(c.Request, c.Writer)
if err != nil {
log.Fatal(err) log.Fatal(err)
} }
c.Redirect(http.StatusFound, "/login") c.Redirect(http.StatusFound, "/login")

88
config/metrics.json Normal file
View File

@@ -0,0 +1,88 @@
{
"GameMetrics": [
{
"NormalizeFactor": 17.7,
"TopWeaponCount": 3,
"GameName": "BF5",
"WeaponMetrics": [
{
"WeaponCategory": "Semi-auto rifle",
"AccuracyFactor": 1.42,
"KpmFactor": 1.19
},
{
"WeaponCategory": "Self-loading rifle",
"AccuracyFactor": 2.05,
"KpmFactor": 1.19
},
{
"WeaponCategory": "Bolt action rifle",
"AccuracyFactor": 2.07,
"KpmFactor": 1.0
},
{
"WeaponCategory": "Shotgun",
"AccuracyFactor": 5.59,
"KpmFactor": 0.934
},
{
"WeaponCategory": "Assault Rifles",
"AccuracyFactor": 1.09,
"KpmFactor": 1.04
},
{
"WeaponCategory": "Pistol carbine",
"AccuracyFactor": 1.13,
"KpmFactor": 1.14
},
{
"WeaponCategory": "Bolt action carbine",
"AccuracyFactor": 1.74,
"KpmFactor": 0.737
},
{
"WeaponCategory": "Smg",
"AccuracyFactor": 0.966,
"KpmFactor": 1.04
},
{
"WeaponCategory": "LMG",
"AccuracyFactor": 0.944,
"KpmFactor": 0.919
},
{
"WeaponCategory": "Assault Rifle",
"AccuracyFactor": 1.09,
"KpmFactor": 1.04
},
{
"WeaponCategory": "Bolt Action",
"AccuracyFactor": 2.07,
"KpmFactor": 1.0
}
]
},
{
"NormalizeFactor": 17.8,
"TopWeaponCount": 3,
"GameName": "BF2042",
"WeaponMetrics": [
{
"WeaponCategory": "Assault Rifles",
"AccuracyFactor": 1.0,
"KpmFactor": 1.0
},
{
"WeaponCategory": "LMG",
"AccuracyFactor": 0.8,
"KpmFactor": 1.238
},
{
"WeaponCategory": "PDW",
"AccuracyFactor": 0.92,
"KpmFactor": 0.963
}
]
}
]
}

View File

@@ -3,37 +3,29 @@ package controllers
import ( import (
"InfantrySkillCalculator/models" "InfantrySkillCalculator/models"
"InfantrySkillCalculator/utils" "InfantrySkillCalculator/utils"
"context"
"errors"
"fmt" "fmt"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/redis/go-redis/v9" "internal/cache"
"gorm.io/gorm" "log"
"gorm.io/gorm/clause"
"net/http" "net/http"
"time"
) )
type AddCacheInput struct { type AddCacheInput struct {
PlayerID uint `json:"player_id" binding:"required"` PlayerID uint `json:"player_id" binding:"required"`
CacheDate time.Time `json:"date" binding:"required"`
Score float32 `json:"score" gorm:"default:-1.0"` Score float32 `json:"score" gorm:"default:-1.0"`
Game string `json:"game" binding:"required"` GameTag string `json:"game_tag" binding:"required"`
} }
type UpdateCacheInput struct { type UpdateCacheInput struct {
CacheDate time.Time `json:"date" binding:"required"`
Score float32 `json:"score" gorm:"default:-1.0"` Score float32 `json:"score" gorm:"default:-1.0"`
} }
var ctx = context.Background() // GetCacheByPlayerID GET /cache/:player_id?game_tag=TAG
// GetCacheByPlayerID GET /cache/:player_id?game_id=TAG
func GetCacheByPlayerID(c *gin.Context) { func GetCacheByPlayerID(c *gin.Context) {
playerId := utils.StringToUint(c.Param("player_id")) playerId := utils.StringToUint(c.Param("player_id"))
gameId := utils.StringToUint(c.Request.URL.Query().Get("game_id")) gameTag := c.Request.URL.Query().Get("game_tag")
val, err := GetCacheByPlayerIDGorm(playerId, gameId) val, err := cache.GetScore(playerId, gameTag)
if err != nil { if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
} else { } else {
@@ -41,19 +33,6 @@ func GetCacheByPlayerID(c *gin.Context) {
} }
} }
func GetCacheByPlayerIDGorm(playerId uint, gameId uint) (float32, error) {
key := fmt.Sprintf("player:%d:game:%d", playerId, gameId)
val, err := models.Cache.Get(ctx, key).Result()
if errors.Is(err, redis.Nil) {
return -1.0, nil // cache miss
} else if err != nil {
return -1.0, err // cache error
} else {
return utils.StringToFloat(val), nil // cache hit
}
}
// AddCache POST /cache // AddCache POST /cache
func AddCache(c *gin.Context) { func AddCache(c *gin.Context) {
var input AddCacheInput var input AddCacheInput
@@ -63,69 +42,84 @@ func AddCache(c *gin.Context) {
} }
var game models.Game var game models.Game
if err := FindGameByTag(&game, input.Game).Error; err != nil { if err := FindGameByTag(&game, input.GameTag).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Game not found!"}) c.JSON(http.StatusBadRequest, gin.H{"error": "Game not found!"})
return return
} }
cache := models.PlayerCache{CacheDate: input.CacheDate, PlayerID: input.PlayerID, Score: input.Score, Game: input.Game} err := cache.SetScore(input.PlayerID, input.GameTag, input.Score)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Cache update failed! Error: " + err.Error()})
return
}
c.JSON(http.StatusOK, cache) c.JSON(http.StatusOK, nil)
} }
// UpdateCacheByPlayerID PATCH /cache/:id?game=TAG // UpdateCacheByPlayerID PATCH /cache/:id?game=TAG
func UpdateCacheByPlayerID(c *gin.Context) { func UpdateCacheByPlayerID(c *gin.Context) {
var cache models.PlayerCache playerID := utils.StringToUint(c.Param("id"))
if err := FindCacheGin(&cache, c).Error; err != nil { gameTag := c.Request.URL.Query().Get("game")
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"}) score := utils.StringToFloat(c.PostForm("score"))
return
err := cache.SetScore(playerID, gameTag, score)
if err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "Cache update failed! Error: " + err.Error()})
} else {
c.JSON(http.StatusOK, nil)
} }
var input UpdateCacheInput
if err := c.ShouldBindJSON(&input); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return
}
models.DB.Model(&cache).Updates(map[string]interface{}{
"CacheDate": input.CacheDate,
"Score": input.Score,
})
c.JSON(http.StatusOK, cache)
} }
// DeleteCacheByPlayerID DELETE /cache/:id?game=TAG // DeleteCacheByPlayerID DELETE /cache/:id?game=TAG
func DeleteCacheByPlayerID(c *gin.Context) { func DeleteCacheByPlayerID(c *gin.Context) {
var cache models.PlayerCache playerID := utils.StringToUint(c.Param("id"))
if err := FindCacheGin(&cache, c).Error; err != nil { gameTag := c.Request.URL.Query().Get("game")
c.JSON(http.StatusBadRequest, gin.H{"error": "Record not found!"})
return if err := cache.DeleteScore(playerID, gameTag); err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
} else {
c.JSON(http.StatusOK, nil)
} }
models.DB.Delete(&cache)
c.JSON(http.StatusOK, true)
} }
// DeleteAllCaches DELETE /cache // DeleteAllCaches DELETE /cache
func DeleteAllCaches(c *gin.Context) { func DeleteAllCaches(c *gin.Context) {
var caches []models.PlayerCache if err := cache.PurgeCache(); err != nil {
c.String(http.StatusBadRequest, err.Error())
} else {
c.String(http.StatusOK, "Purged all caches!")
}
}
// GetScoreByPlayerID GET /score/:player_id?game_tag=TAG
func GetScoreByPlayerID(c *gin.Context) {
var player models.Player
var gameTag = c.Request.URL.Query().Get("game_tag")
var playerId = utils.StringToUint(c.Param("player_id"))
if err := models.DB. if err := models.DB.
Session(&gorm.Session{AllowGlobalUpdate: true}). Model(&models.Player{}).
Clauses(clause.Returning{}). Where("id = ?", playerId).
Delete(&caches).Error; err != nil { First(&player).Error; err != nil {
c.String(http.StatusBadRequest, "Purge failed! Error: "+err.Error())
c.JSON(http.StatusBadRequest, gin.H{"error": "Player not found!"})
return return
} }
c.String(http.StatusOK, "Purged "+utils.UintToString(uint(len(caches)))+" caches!") score, err := cache.GetScore(player.ID, gameTag)
} if err != nil || score == -1 {
score = utils.CalcPlayerScore(player.Name, gameTag)
if score == score && score != -1 { // not NaN
if err := cache.SetScore(player.ID, gameTag, score); err != nil {
log.Fatal(err)
}
}
}
func FindCacheGin(out interface{}, c *gin.Context) *gorm.DB { if score != score || score == -1 { // NaN
return FindCache(out, utils.StringToUint(c.Param("id")), c.Request.URL.Query().Get("game")) c.String(http.StatusOK, "<i class=\"bi bi-person-x-fill me-2 text-danger fs-5\" style=\"margin-left: 0.69rem;\"></i>")
} } else if score != -1 {
c.String(http.StatusOK, fmt.Sprintf("%.2f", score))
func FindCache(out interface{}, id uint, game string) *gorm.DB { } else {
return models.DB.Where("player_id = ?", id).Where("game = ?", game).First(out) c.JSON(http.StatusBadRequest, "Invalid request!")
}
} }

View File

@@ -0,0 +1,32 @@
package controllers
import (
"InfantrySkillCalculator/models"
"InfantrySkillCalculator/utils"
"encoding/json"
"io"
"log"
"os"
)
func LoadMetrics() {
f, err := os.Open("./config/metrics.json")
if err != nil {
log.Fatal("Failed to open metrics.json: ", err)
}
defer func(f *os.File) {
_ = f.Close()
}(f)
data, err := io.ReadAll(f)
if err != nil {
log.Fatal("Failed to read metrics.json: ", err)
}
var metrics models.GameMetrics
if err := json.Unmarshal(data, &metrics); err != nil {
log.Fatal("Failed to deserialize metrics.json: ", err)
}
utils.GameMetrics = metrics
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/clause" "gorm.io/gorm/clause"
"internal/cache"
"log" "log"
"net/http" "net/http"
"os" "os"
@@ -37,24 +38,36 @@ func GetPlayersByClanHTML(c *gin.Context) {
if err := models.DB. if err := models.DB.
Where("clan_id = ?", utils.StringToUint(clanId)). Where("clan_id = ?", utils.StringToUint(clanId)).
Find(&players).Error; err != nil { Find(&players).Error; err != nil {
c.String(http.StatusBadRequest, "")
log.Fatal(err)
return return
} }
file, err := os.ReadFile("./templates/player_list_item.html") file, err := os.ReadFile("./templates/player_list_item.html")
if err != nil { if err != nil {
c.String(http.StatusBadRequest, "")
log.Fatal(err)
return return
} }
playerItem := string(file) playerItem := string(file)
game, err := GetActiveGame(c)
if err != nil {
c.String(http.StatusBadRequest, "")
log.Fatal(err)
return
}
var htmlOptions string var htmlOptions string
for _, player := range players { for _, player := range players {
var score string var score string
if val, err := GetCacheByPlayerIDGorm(player.ID, 0); err != nil { if val, err := cache.GetScore(player.ID, game.Tag); err != nil || val == -1.0 {
score = "----" score = "<i class=\"bi bi-dash me-2\" style=\"margin-left:0.7rem;\"></i>"
} else { } else {
score = utils.FloatToString(val) score = fmt.Sprintf("%.2f", val)
} }
htmlOptions += fmt.Sprintf(playerItem, player.Name, score, player.ID, player.ID) htmlOptions += fmt.Sprintf(playerItem, player.Name, score, utils.UintToString(player.ID)+"?game_tag="+game.Tag, player.ID, player.ID)
} }
c.Header("Content-Type", "text/html") c.Header("Content-Type", "text/html")

View File

@@ -2,9 +2,10 @@ package controllers
import ( import (
"InfantrySkillCalculator/models" "InfantrySkillCalculator/models"
"InfantrySkillCalculator/utils" "errors"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"net/http" "net/http"
"session"
) )
type UpdateSettingsInput struct { type UpdateSettingsInput struct {
@@ -18,9 +19,11 @@ type UpdateSettingsInput struct {
func GetSettings(c *gin.Context) { func GetSettings(c *gin.Context) {
var settings models.UserSettings var settings models.UserSettings
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) username, ok := session.GetUsername(c)
var username string if !ok {
username, _ = session.Values["username"].(string) c.JSON(http.StatusBadRequest, gin.H{"error": "Not logged in!"})
return
}
if err := models.DB.Where("username = ?", username).First(&settings).Error; err != nil { if err := models.DB.Where("username = ?", username).First(&settings).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "No settings available!"}) c.JSON(http.StatusBadRequest, gin.H{"error": "No settings available!"})
@@ -37,13 +40,39 @@ func GetSettings(c *gin.Context) {
c.JSON(http.StatusOK, sanitizedSettings) c.JSON(http.StatusOK, sanitizedSettings)
} }
func GetActiveGame(c *gin.Context) (models.Game, error) {
var settings models.UserSettings
var game models.Game
username, ok := session.GetUsername(c)
if !ok {
err := errors.New("not logged in")
c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()})
return models.Game{}, err
}
if err := models.DB.Where("username = ?", username).First(&settings).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "No settings available!"})
return models.Game{}, err
}
if err := models.DB.Where("id = ?", settings.ActiveGameID).First(&game).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "No active game available!"})
return models.Game{}, err
}
return game, nil
}
// UpdateSettings PATCH /settings // UpdateSettings PATCH /settings
func UpdateSettings(c *gin.Context) { func UpdateSettings(c *gin.Context) {
var settings models.UserSettings var settings models.UserSettings
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) username, ok := session.GetUsername(c)
var username string if !ok {
username, _ = session.Values["username"].(string) c.JSON(http.StatusBadRequest, gin.H{"error": "Not logged in!"})
return
}
if err := models.DB.Where("username = ?", username).First(&settings).Error; err != nil { if err := models.DB.Where("username = ?", username).First(&settings).Error; err != nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "No settings available!"}) c.JSON(http.StatusBadRequest, gin.H{"error": "No settings available!"})

18
docker-compose.yml Normal file
View File

@@ -0,0 +1,18 @@
version: '3'
services:
isc:
image: docker.maximilian.link/isc:dev
ports:
- "127.0.0.1:8000:8000"
environment:
- GO_ENV=production
- REDIS_ADDRESS=redis
depends_on:
- redis
volumes:
- ./app:/app
redis:
image: redis:alpine
ports:
- "127.0.0.1:6379:6379"

21
go.mod
View File

@@ -7,21 +7,34 @@ require (
github.com/gorilla/sessions v1.2.2 github.com/gorilla/sessions v1.2.2
github.com/redis/go-redis/v9 v9.4.0 github.com/redis/go-redis/v9 v9.4.0
golang.org/x/crypto v0.9.0 golang.org/x/crypto v0.9.0
gorm.io/driver/sqlite v1.5.4
gorm.io/gorm v1.25.5 gorm.io/gorm v1.25.5
) )
require internal/cache v1.0.0
replace internal/cache => ./internal/cache
require (
github.com/glebarez/sqlite v1.10.0
internal/session v1.0.0
)
replace internal/session => ./internal/session
require ( require (
github.com/bytedance/sonic v1.9.1 // indirect github.com/bytedance/sonic v1.9.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-playground/locales v0.14.1 // indirect github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect github.com/goccy/go-json v0.10.2 // indirect
github.com/google/uuid v1.3.0 // indirect
github.com/gorilla/securecookie v1.1.2 // indirect github.com/gorilla/securecookie v1.1.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect github.com/jinzhu/now v1.1.5 // indirect
@@ -29,10 +42,10 @@ require (
github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.17 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
@@ -41,4 +54,8 @@ require (
golang.org/x/text v0.9.0 // indirect golang.org/x/text v0.9.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect google.golang.org/protobuf v1.30.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.22.5 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.23.1 // indirect
) )

28
go.sum
View File

@@ -15,12 +15,18 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU= github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA= github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE= github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE=
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg= github.com/gin-gonic/gin v1.9.1 h1:4idEAncQnU5cB7BeOkPtxjfCSye0AAm1R0RVIqJ+Jmg=
github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU= github.com/gin-gonic/gin v1.9.1/go.mod h1:hPrL7YrpYKXt5YId3A/Tnip5kqbEAP+KLuI3SUcPTeU=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.10.0 h1:u4gt8y7OND/cCei/NMHmfbLxF6xP2wgKcT/BJf2pYkc=
github.com/glebarez/sqlite v1.10.0/go.mod h1:IJ+lfSOmiekhQsFTJRx/lHtGYmCdtAiTaf5wI9u5uHA=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -37,6 +43,10 @@ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA= github.com/gorilla/securecookie v1.1.2 h1:YCIWL56dvtr73r6715mJs5ZvhtnY73hBvEF8kXD8ePA=
github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo= github.com/gorilla/securecookie v1.1.2/go.mod h1:NfCASbcHqRSY+3a8tlWJwsQap2VX5pwzwo4h3eOamfo=
github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY= github.com/gorilla/sessions v1.2.2 h1:lqzMYz6bOfvn2WriPUjNByzeXIlVzURcPmgMczkmTjY=
@@ -54,8 +64,6 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4= github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-sqlite3 v1.14.17 h1:mCRHCLDUBXgpKAqIKsaAaAsrAlbkeomtRFKXh2L6YIM=
github.com/mattn/go-sqlite3 v1.14.17/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -67,6 +75,9 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk= github.com/redis/go-redis/v9 v9.4.0 h1:Yzoz33UZw9I/mFhx4MNrB6Fk+XHO1VukNcCa1+lwyKk=
github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M= github.com/redis/go-redis/v9 v9.4.0/go.mod h1:hdY0cQFCN4fnSYT6TkisLufl/4W5UIXyv0b/CLO2V2M=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
@@ -95,8 +106,9 @@ golang.org/x/sys v0.8.0 h1:EBmGv8NaZBZTWvrbjNoL6HVt+IVy3QDQpJs7VRIw3tU=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
@@ -105,8 +117,14 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/sqlite v1.5.4 h1:IqXwXi8M/ZlPzH/947tn5uik3aYQslP9BVveoax0nV0=
gorm.io/driver/sqlite v1.5.4/go.mod h1:qxAuCol+2r6PannQDpOP1FP6ag3mKi4esLnB/jHed+4=
gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls= gorm.io/gorm v1.25.5 h1:zR9lOiiYf09VNh5Q1gphfyia1JpiClIWG9hQaxB/mls=
gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= gorm.io/gorm v1.25.5/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
modernc.org/libc v1.22.5 h1:91BNch/e5B0uPbJFgqbxXuOnxBQjlS//icfQEGmvyjE=
modernc.org/libc v1.22.5/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=
rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4=

7
go.work Normal file
View File

@@ -0,0 +1,7 @@
go 1.21
use (
.
internal/cache
internal/session
)

31
go.work.sum Normal file
View File

@@ -0,0 +1,31 @@
github.com/golang/protobuf v1.5.0 h1:LUVKkCeviFUMKqHa4tXIIij/lbhnMbP7Fn5wKdKkRh4=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/term v0.8.0 h1:n5xxQn2i3PC0yLAbjTpNT85q/Kgzcr2gIoX9OrJUols=
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM=
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI=
lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk=
modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw=
modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0=
modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw=
modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY=
modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM=
modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM=
modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4=
modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0=
modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY=
modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw=
modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY=
modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c=
modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg=
modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY=
modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE=
rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4=

55
internal/cache/cache.go vendored Normal file
View File

@@ -0,0 +1,55 @@
package cache
import (
"InfantrySkillCalculator/models"
"InfantrySkillCalculator/utils"
"context"
"errors"
"fmt"
"github.com/redis/go-redis/v9"
)
var ctx = context.Background()
func GetValue(key string) (string, error) {
val, err := models.Cache.Get(ctx, key).Result()
if err != nil {
return "", err // cache miss or error
} else {
return val, nil // cache hit
}
}
func SetValue(key string, value interface{}) error {
return models.Cache.Set(ctx, key, value, utils.PlayerCacheLifetime).Err()
}
func SetScore(playerId uint, gameTag string, score float32) error {
key := GetPlayerCacheKey(playerId, gameTag)
return SetValue(key, score)
}
func GetScore(playerId uint, gameTag string) (float32, error) {
key := GetPlayerCacheKey(playerId, gameTag)
val, err := GetValue(key)
if errors.Is(err, redis.Nil) {
return -1.0, nil // cache miss
} else if err != nil {
return -1.0, err // cache error
} else {
return utils.StringToFloat(val), nil // cache hit
}
}
func DeleteScore(playerId uint, gameTag string) error {
key := GetPlayerCacheKey(playerId, gameTag)
return models.Cache.Del(ctx, key).Err()
}
func PurgeCache() error {
return models.Cache.FlushAll(ctx).Err()
}
func GetPlayerCacheKey(playerId uint, gameTag string) string {
return fmt.Sprintf("player:%d:game:%s", playerId, gameTag)
}

3
internal/cache/go.mod vendored Normal file
View File

@@ -0,0 +1,3 @@
module cache
go 1.21

3
internal/session/go.mod Normal file
View File

@@ -0,0 +1,3 @@
module session
go 1.21

View File

@@ -0,0 +1,43 @@
package session
import (
"github.com/gin-gonic/gin"
"github.com/gorilla/sessions"
)
var store = sessions.NewCookieStore([]byte("f0q0qew0!)§(ds9713lsda231"))
const LoginSessionName = "session"
func GetUsername(c *gin.Context) (string, bool) {
session, _ := getSession(c)
username, ok := session.Values["username"].(string)
return username, ok
}
func GetAuthenticated(c *gin.Context) (bool, bool) {
session, _ := getSession(c)
auth, ok := session.Values["authenticated"].(bool)
return auth, ok
}
func getSession(c *gin.Context) (*sessions.Session, error) {
return store.Get(c.Request, LoginSessionName)
}
func InvalidateSession(c *gin.Context) error {
session, _ := getSession(c)
session.Options.MaxAge = -1
err := session.Save(c.Request, c.Writer)
return err
}
func SetLoginSession(username string, c *gin.Context) error {
session, _ := getSession(c)
session.Values["authenticated"] = true
session.Values["username"] = username
err := session.Save(c.Request, c.Writer)
return err
}

15
main.go
View File

@@ -53,10 +53,14 @@ func init() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
controllers.LoadMetrics()
} }
func main() { func main() {
//gin.SetMode(gin.ReleaseMode) // uncomment upon release if os.Getenv("GO_ENV") == "production" {
gin.SetMode(gin.ReleaseMode)
}
router := gin.New() router := gin.New()
err := router.SetTrustedProxies([]string{"127.0.0.1"}) err := router.SetTrustedProxies([]string{"127.0.0.1"})
@@ -72,6 +76,13 @@ func main() {
models.ConnectDatabase() models.ConnectDatabase()
models.ConnectCache() models.ConnectCache()
var code models.ActivationCode
if err := models.DB.First(&code).Error; err != nil {
firstCode := utils.GenerateActivationCode()
models.DB.Create(&models.ActivationCode{Code: firstCode, UserRole: models.AdminRole})
log.Println("Created first activation code with ADMIN role:\n" + firstCode)
}
f, _ := os.OpenFile("isc_rest.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660) f, _ := os.OpenFile("isc_rest.log", os.O_RDWR|os.O_APPEND|os.O_CREATE, 0660)
utils.GinWriter = io.MultiWriter(f, os.Stdout) utils.GinWriter = io.MultiWriter(f, os.Stdout)
router.Use( router.Use(
@@ -114,6 +125,8 @@ func main() {
protected.GET("/cache/:player_id", controllers.GetCacheByPlayerID) protected.GET("/cache/:player_id", controllers.GetCacheByPlayerID)
protected.GET("/score/:player_id", controllers.GetScoreByPlayerID)
protected.GET("/game", controllers.GetGames) protected.GET("/game", controllers.GetGames)
protected.GET("/game_html", controllers.GetGamesHTML) protected.GET("/game_html", controllers.GetGamesHTML)

View File

@@ -1,11 +0,0 @@
package models
import "time"
type PlayerCache struct {
CacheID uint `json:"cache_id" gorm:"primary_key"`
PlayerID uint `json:"player_id"`
CacheDate time.Time `json:"date"`
Score float32 `json:"score" gorm:"default:-1.0"`
Game string `json:"game"`
}

View File

@@ -1,9 +0,0 @@
package models
type MetricSetting struct {
ID uint `json:"id" gorm:"primary_key"`
Name string `json:"name" binding:"required"`
WeaponCategory string `json:"weapon_category" binding:"required"`
Value float64 `json:"value" binding:"required"`
Game string `json:"game" binding:"required"`
}

18
models/metrics_json.go Normal file
View File

@@ -0,0 +1,18 @@
package models
type GameMetrics struct {
GameMetrics []GameMetric
}
type GameMetric struct {
NormalizeFactor float64
TopWeaponCount int
GameName string
WeaponMetrics []WeaponMetric
}
type WeaponMetric struct {
WeaponCategory string
AccuracyFactor float64
KpmFactor float64
}

View File

@@ -1,13 +1,13 @@
package models package models
import ( import (
"InfantrySkillCalculator/utils"
"context" "context"
"github.com/glebarez/sqlite"
"github.com/redis/go-redis/v9" "github.com/redis/go-redis/v9"
"gorm.io/driver/sqlite"
"gorm.io/gorm" "gorm.io/gorm"
"gorm.io/gorm/logger" "gorm.io/gorm/logger"
"log" "log"
"os"
) )
var DB *gorm.DB var DB *gorm.DB
@@ -33,10 +33,6 @@ func ConnectDatabase() {
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} }
err = database.AutoMigrate(&PlayerCache{})
if err != nil {
log.Fatal(err)
}
err = database.AutoMigrate(&User{}) err = database.AutoMigrate(&User{})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -44,13 +40,6 @@ func ConnectDatabase() {
err = database.AutoMigrate(&ActivationCode{}) err = database.AutoMigrate(&ActivationCode{})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
} else {
var code ActivationCode
if err := database.First(&code).Error; err != nil {
firstCode := utils.GenerateActivationCode()
database.Create(&ActivationCode{Code: firstCode, UserRole: AdminRole})
log.Println("Created first activation code with ADMIN role:\n" + firstCode)
}
} }
err = database.AutoMigrate(&Game{}) err = database.AutoMigrate(&Game{})
if err != nil { if err != nil {
@@ -64,10 +53,6 @@ func ConnectDatabase() {
} }
} }
err = database.AutoMigrate(&MetricSetting{})
if err != nil {
log.Fatal(err)
}
err = database.AutoMigrate(&UserSettings{}) err = database.AutoMigrate(&UserSettings{})
if err != nil { if err != nil {
log.Fatal(err) log.Fatal(err)
@@ -77,8 +62,17 @@ func ConnectDatabase() {
} }
func ConnectCache() { func ConnectCache() {
address := os.Getenv("REDIS_ADDRESS")
if address == "" {
address = "127.0.0.1"
}
port := os.Getenv("REDIS_PORT")
if port == "" {
port = "6379"
}
Cache = redis.NewClient(&redis.Options{ Cache = redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379", Addr: address + ":" + port,
Password: "", Password: "",
DB: 0, DB: 0,
}) })

16
models/tracker_json.go Normal file
View File

@@ -0,0 +1,16 @@
package models
type TrackerWeaponJSON struct {
Weapons []Weapon `json:"weapons"`
}
type Weapon struct {
Type string `json:"type"`
Name string `json:"weaponName"`
ID string `json:"id"`
Kills int `json:"kills"`
KPM float64 `json:"killsPerMinute"`
ShotsFired int `json:"shotsFired"`
ShotsHit int `json:"shotsHit"`
Accuracy float64 `json:"-"`
}

View File

@@ -2,10 +2,10 @@ package main
import ( import (
"InfantrySkillCalculator/controllers" "InfantrySkillCalculator/controllers"
"InfantrySkillCalculator/utils"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"log" "log"
"net/http" "net/http"
"session"
) )
func mainPage(c *gin.Context) { func mainPage(c *gin.Context) {
@@ -20,9 +20,7 @@ func mainPage(c *gin.Context) {
} }
func loginPage(c *gin.Context) { func loginPage(c *gin.Context) {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) if auth, ok := session.GetAuthenticated(c); ok && auth {
if auth, ok := session.Values["authenticated"].(bool); ok && auth {
c.Redirect(http.StatusFound, "/") c.Redirect(http.StatusFound, "/")
return return
} }
@@ -42,11 +40,7 @@ func loginPost(c *gin.Context) {
return return
} }
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) if err := session.SetLoginSession(username, c); err != nil {
session.Values["authenticated"] = true
session.Values["username"] = username
err := session.Save(c.Request, c.Writer)
if err != nil {
c.JSON(http.StatusInternalServerError, nil) c.JSON(http.StatusInternalServerError, nil)
return return
} }
@@ -56,10 +50,7 @@ func loginPost(c *gin.Context) {
} }
func logout(c *gin.Context) { func logout(c *gin.Context) {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) if err := session.InvalidateSession(c); err != nil {
session.Values["authenticated"] = false
err := session.Save(c.Request, c.Writer)
if err != nil {
c.JSON(http.StatusInternalServerError, nil) c.JSON(http.StatusInternalServerError, nil)
return return
} }
@@ -68,9 +59,7 @@ func logout(c *gin.Context) {
} }
func registerPage(c *gin.Context) { func registerPage(c *gin.Context) {
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) if auth, ok := session.GetAuthenticated(c); ok && auth {
if auth, ok := session.Values["authenticated"].(bool); ok && auth {
c.Redirect(http.StatusFound, "/") c.Redirect(http.StatusFound, "/")
return return
} }
@@ -99,11 +88,7 @@ func registerPost(c *gin.Context) {
controllers.CreateUser(username, hashedPassword, true, code) controllers.CreateUser(username, hashedPassword, true, code)
session, _ := utils.Store.Get(c.Request, utils.LoginSessionName) if err := session.SetLoginSession(username, c); err != nil {
session.Values["authenticated"] = true
session.Values["username"] = username
err = session.Save(c.Request, c.Writer)
if err != nil {
c.JSON(http.StatusInternalServerError, nil) c.JSON(http.StatusInternalServerError, nil)
return return
} }

View File

@@ -52,4 +52,3 @@ body, html {
height: auto; /* Adjust as needed */ height: auto; /* Adjust as needed */
max-height: 300px; /* Adjust as needed */ max-height: 300px; /* Adjust as needed */
} }

View File

@@ -1,5 +1,5 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]'); const tooltipTriggerList = document.querySelectorAll('[config-bs-toggle="tooltip"]');
tooltipTriggerList.forEach((elem) => { tooltipTriggerList.forEach((elem) => {
new bootstrap.Tooltip(elem); new bootstrap.Tooltip(elem);
}); });
@@ -180,3 +180,13 @@ function createCodeDialog(btn) {
} }
}); });
} }
function singleCalcSpinner(sender) {
const spinner = '<i class="spinner-grow spinner-grow-sm text-info align-baseline me-2" style="margin-left: 0.91rem;" role="status"></i>';
const score = sender.previousElementSibling.children[1];
score.innerHTML = spinner;
sender.disabled = true;
sender.addEventListener('htmx:afterRequest', function () {
sender.disabled = false;
}, {once: true});
}

View File

@@ -61,13 +61,13 @@
</div> </div>
</div> </div>
<div class="col-auto position-absolute start-50 translate-middle-x"> <div class="col-auto position-absolute start-50 translate-middle-x">
<button class="btn btn-lg btn-outline-primary"> <button class="btn btn-lg btn-outline-primary" type="button" disabled>
<i class="bi bi-calculator-fill me-2"></i> <i class="bi bi-calculator-fill me-2"></i>
Berechnen Berechnen
</button> </button>
</div> </div>
<div class="col-auto position-absolute end-0"> <div class="col-auto position-absolute end-0">
<button class="btn btn-lg btn-outline-secondary text-secondary-emphasis"> <button class="btn btn-lg btn-outline-secondary text-secondary-emphasis" type="button" disabled>
<i class="bi bi-person me-2"></i> <i class="bi bi-person me-2"></i>
Einzel-Abfrage Einzel-Abfrage
</button> </button>

View File

@@ -86,7 +86,7 @@
} }
function createSubmitClanHandler(modalEvent) { function createSubmitClanHandler(modalEvent) {
return function submitClanHandler(e) { return function submitClanHandler(_) {
const [clanList, otherClanList] = getClanLists(modalEvent); const [clanList, otherClanList] = getClanLists(modalEvent);
if (!validateInputs()) { if (!validateInputs()) {
@@ -130,7 +130,7 @@
submitButton.addEventListener('click', submitClanHandler); submitButton.addEventListener('click', submitClanHandler);
}); });
addClanModal.addEventListener('hidden.bs.modal', event => { addClanModal.addEventListener('hidden.bs.modal', _ => {
submitButton.removeEventListener('click', submitClanHandler); submitButton.removeEventListener('click', submitClanHandler);
clanName.value = ""; clanName.value = "";

View File

@@ -38,8 +38,8 @@
const playerName = addPlayerModal.querySelector('#playerName'); const playerName = addPlayerModal.querySelector('#playerName');
const clanName = addPlayerModal.querySelector('#playerClanName'); const clanName = addPlayerModal.querySelector('#playerClanName');
const errorDiv = addPlayerModal.querySelector('.error-message'); const errorDiv = addPlayerModal.querySelector('.error-message');
const homeClanListIndex = document.getElementById('home-clan').selectedIndex; const homeClanList = document.getElementById('home-clan');
const oppClanListIndex = document.getElementById('opponent-clan').selectedIndex; const oppClanList = document.getElementById('opponent-clan');
function validateInput() { function validateInput() {
if (playerName.value.length < 1) { if (playerName.value.length < 1) {
@@ -50,8 +50,8 @@
return true; return true;
} }
function createSubmitPlayerHandler(modalEvent) { function createSubmitPlayerHandler(_) {
return function submitPlayerHandler(e) { return function submitPlayerHandler(_) {
if (!validateInput()) if (!validateInput())
return; return;
@@ -74,7 +74,7 @@
return response.text(); return response.text();
}) })
.then(() => { .then(() => {
const sameClan = homeClanListIndex === oppClanListIndex; const sameClan = homeClanList.selectedIndex === oppClanList.selectedIndex;
if (playerList.id === 'home-player-list' || sameClan) if (playerList.id === 'home-player-list' || sameClan)
htmx.ajax('GET', '/players_html', {target: '#home-player-list', values: {"clan_id": getSelectedClanId("home-clan")}}); htmx.ajax('GET', '/players_html', {target: '#home-player-list', values: {"clan_id": getSelectedClanId("home-clan")}});
if (playerList.id === 'opponent-player-list' || sameClan) if (playerList.id === 'opponent-player-list' || sameClan)
@@ -98,7 +98,7 @@
clanName.value = selectedClan.innerText; clanName.value = selectedClan.innerText;
}); });
addPlayerModal.addEventListener('hidden.bs.modal', event => { addPlayerModal.addEventListener('hidden.bs.modal', _ => {
submitButton.removeEventListener('click', submitPlayerHandler); submitButton.removeEventListener('click', submitPlayerHandler);
playerName.value = ""; playerName.value = "";

View File

@@ -24,6 +24,7 @@
document.addEventListener('DOMContentLoaded', function() { document.addEventListener('DOMContentLoaded', function() {
const deleteClanModal = document.getElementById('deleteClanModal') const deleteClanModal = document.getElementById('deleteClanModal')
const deleteClanModalBS = new bootstrap.Modal('#deleteClanModal'); const deleteClanModalBS = new bootstrap.Modal('#deleteClanModal');
if (deleteClanModal) { if (deleteClanModal) {
deleteClanModal.addEventListener('show.bs.modal', event => { deleteClanModal.addEventListener('show.bs.modal', event => {
const [clanList, otherClanList] = getClanLists(event); const [clanList, otherClanList] = getClanLists(event);

View File

@@ -34,8 +34,8 @@
modalBodyInput.innerText = selectedPlayer; modalBodyInput.innerText = selectedPlayer;
const playerListId = button.closest('ul').parentElement.parentElement.id; const playerListId = button.closest('ul').parentElement.parentElement.id;
const homeClanListIndex = document.getElementById('home-clan').selectedIndex; const homeClanList = document.getElementById('home-clan');
const oppClanListIndex = document.getElementById('opponent-clan').selectedIndex; const oppClanList = document.getElementById('opponent-clan');
const submitButton = deletePlayerModal.querySelector('button[name="submit"]'); const submitButton = deletePlayerModal.querySelector('button[name="submit"]');
submitButton.addEventListener('click', function () { submitButton.addEventListener('click', function () {
@@ -46,7 +46,7 @@
} }
}) })
.then(() => { .then(() => {
const sameClan = homeClanListIndex === oppClanListIndex; const sameClan = homeClanList.selectedIndex === oppClanList.selectedIndex;
if (playerListId === 'home-player-list' || sameClan) if (playerListId === 'home-player-list' || sameClan)
htmx.ajax('GET', '/players_html', {target: '#home-player-list', values: {"clan_id": getSelectedClanId("home-clan")}}); htmx.ajax('GET', '/players_html', {target: '#home-player-list', values: {"clan_id": getSelectedClanId("home-clan")}});
if (playerListId === 'opponent-player-list' || sameClan) if (playerListId === 'opponent-player-list' || sameClan)

View File

@@ -87,7 +87,7 @@
} }
function createSubmitClanHandler() { function createSubmitClanHandler() {
return function submitClanHandler(e) { return function submitClanHandler(_) {
if (!validateInputs()) if (!validateInputs())
return; return;
@@ -149,7 +149,7 @@
}); });
}); });
editClanModal.addEventListener('hidden.bs.modal', event => { editClanModal.addEventListener('hidden.bs.modal', _ => {
submitButton.removeEventListener('click', submitClanHandler); submitButton.removeEventListener('click', submitClanHandler);
clanName.value = ""; clanName.value = "";

View File

@@ -34,8 +34,8 @@
const submitButton = editPlayerModal.querySelector('button[name="submit"]'); const submitButton = editPlayerModal.querySelector('button[name="submit"]');
const playerName = editPlayerModal.querySelector('#editPlayerName'); const playerName = editPlayerModal.querySelector('#editPlayerName');
const errorDiv = editPlayerModal.querySelector('.error-message'); const errorDiv = editPlayerModal.querySelector('.error-message');
const homeClanListIndex = document.getElementById('home-clan').selectedIndex; const homeClanList = document.getElementById('home-clan');
const oppClanListIndex = document.getElementById('opponent-clan').selectedIndex; const oppClanList = document.getElementById('opponent-clan');
function validateInput() { function validateInput() {
if (playerName.value.length < 1) { if (playerName.value.length < 1) {
@@ -71,7 +71,7 @@
return response.text(); return response.text();
}) })
.then(() => { .then(() => {
const sameClan = homeClanListIndex === oppClanListIndex; const sameClan = homeClanList.selectedIndex === oppClanList.selectedIndex;
if (playerList.id === 'home-player-list' || sameClan) if (playerList.id === 'home-player-list' || sameClan)
htmx.ajax('GET', '/players_html', {target: '#home-player-list', values: {"clan_id": getSelectedClanId("home-clan")}}); htmx.ajax('GET', '/players_html', {target: '#home-player-list', values: {"clan_id": getSelectedClanId("home-clan")}});
if (playerList.id === 'opponent-player-list' || sameClan) if (playerList.id === 'opponent-player-list' || sameClan)
@@ -95,7 +95,7 @@
playerName.value = event.relatedTarget.closest('.input-group').querySelector('span').innerText; playerName.value = event.relatedTarget.closest('.input-group').querySelector('span').innerText;
}); });
editPlayerModal.addEventListener('hide.bs.modal', event => { editPlayerModal.addEventListener('hide.bs.modal', _ => {
submitButton.removeEventListener('click', submitPlayerHandler); submitButton.removeEventListener('click', submitPlayerHandler);
playerName.value = ""; playerName.value = "";

View File

@@ -53,7 +53,7 @@
const useCache = document.getElementById('settingsUseCache'); const useCache = document.getElementById('settingsUseCache');
function createSubmitSettingsHandler() { function createSubmitSettingsHandler() {
return function submitSettingsHandler(e) { return function submitSettingsHandler(_) {
let activeGameId = games.options[games.selectedIndex].value; let activeGameId = games.options[games.selectedIndex].value;
if (activeGameId === '') { if (activeGameId === '') {
alert('Bitte wähle ein Spiel aus.'); alert('Bitte wähle ein Spiel aus.');
@@ -88,7 +88,7 @@
} }
if (settingsModal) { if (settingsModal) {
settingsModal.addEventListener('show.bs.modal', event => { settingsModal.addEventListener('show.bs.modal', _ => {
submitSettingsHandler = createSubmitSettingsHandler(); submitSettingsHandler = createSubmitSettingsHandler();
submitButton.addEventListener('click', submitSettingsHandler); submitButton.addEventListener('click', submitSettingsHandler);

View File

@@ -3,10 +3,10 @@
<input class="form-check-input fs-4 border-secondary mt-0" type="checkbox" value="" onchange="updateSelectedPlayers(this)"> <input class="form-check-input fs-4 border-secondary mt-0" type="checkbox" value="" onchange="updateSelectedPlayers(this)">
</div> </div>
<span class="form-control py-2 px-3" style="width: 10em">%s</span> <span class="form-control py-2 px-3" style="width: 10em">%s</span>
<span class="form-control text-center px-1 text-secondary"> <div class="form-control text-center px-2 text-secondary">
<i class="bi bi-trophy me-2 text-warning"></i>%s <i class="bi bi-trophy me-3 text-warning"></i><span id="quickScore" class="text-secondary-emphasis">%s</span>
</span> </div>
<button type="button" class="btn btn-outline-secondary"> <button type="button" class="btn btn-outline-secondary" hx-get="/score/%s" hx-target="previous #quickScore" onclick="singleCalcSpinner(this)">
<i class="bi bi-calculator text-info"></i> <i class="bi bi-calculator text-info"></i>
</button> </button>
<button class="btn btn-outline-secondary text-secondary-emphasis dropdown-toggle py-1" type="button" data-bs-toggle="dropdown"></button> <button class="btn btn-outline-secondary text-secondary-emphasis dropdown-toggle py-1" type="button" data-bs-toggle="dropdown"></button>

View File

@@ -1,11 +1,11 @@
package utils package utils
import ( import (
"InfantrySkillCalculator/models"
"io" "io"
"time" "time"
) )
var ClanLastChanged time.Time = time.Now().UTC()
var CacheLastChanged time.Time = time.Now().UTC()
var PlayersLastChanged time.Time = time.Now().UTC()
var GinWriter io.Writer = nil var GinWriter io.Writer = nil
var GameMetrics models.GameMetrics
var PlayerCacheLifetime = 24 * time.Hour

129
utils/score.go Normal file
View File

@@ -0,0 +1,129 @@
package utils
import (
"InfantrySkillCalculator/models"
"encoding/json"
"fmt"
"io"
"log"
"math"
"net/http"
"sort"
)
func GetGameMetric(gameTag string) *models.GameMetric {
for _, metric := range GameMetrics.GameMetrics {
if metric.GameName == gameTag {
return &metric
}
}
return nil
}
func FindWeaponMetric(weaponMetrics []models.WeaponMetric, weaponCategory string) *models.WeaponMetric {
for _, metric := range weaponMetrics {
if metric.WeaponCategory == weaponCategory {
return &metric
}
}
return nil
}
func CalcPlayerScore(playerName string, gameTag string) float32 {
if gameTag != "BF5" && gameTag != "BF2042" {
_, _ = fmt.Fprintf(GinWriter, "UNSUPPORTED GAME: "+gameTag+"\n")
return -1
}
gameMetrics := GetGameMetric(gameTag)
if gameMetrics == nil {
_, _ = fmt.Fprintf(GinWriter, "No game metrics specified for '"+gameTag+"'\n")
return -1
}
normalizeFactor := gameMetrics.NormalizeFactor
topWeaponCount := gameMetrics.TopWeaponCount
c := http.Client{}
var reqUri string
if gameTag == "BF5" {
reqUri = "https://api.gametools.network/bfv/weapons/?raw=false&format_values=false&name=" + playerName + "&platform=pc"
} else if gameTag == "BF2042" {
reqUri = "https://api.gametools.network/bf2042/stats/?raw=false&format_values=false&name=" + playerName + "&platform=pc"
}
req, err := http.NewRequest("GET", reqUri, nil)
if err != nil {
_, _ = fmt.Fprintf(GinWriter, err.Error()+"\n")
return -1
}
req.Header.Set("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/109.0.0.0 Safari/537.36")
res, err := c.Do(req)
if err != nil {
_, _ = fmt.Fprintf(GinWriter, err.Error()+"\n")
return -1
}
defer func(Body io.ReadCloser) {
_ = Body.Close()
}(res.Body)
if res.StatusCode == 404 {
_, _ = fmt.Fprintf(GinWriter, "User '"+playerName+"' does not exist!\n")
return -1
} else if res.StatusCode != 200 {
log.Fatalf("Status code error: %d %s", res.StatusCode, res.Status)
}
var data models.TrackerWeaponJSON
var body, _ = io.ReadAll(res.Body)
if err := json.Unmarshal(body, &data); err != nil {
log.Fatalf("Failed to deserialize tracker API response: %s", err)
}
sort.SliceStable(data.Weapons, func(i, j int) bool { return data.Weapons[i].Kills > data.Weapons[j].Kills })
var top []models.Weapon
for _, weapon := range data.Weapons {
if len(top) >= topWeaponCount {
break
}
if gameTag == "BF2042" && weapon.Kills < 100 {
break
}
acc := (float64(weapon.ShotsHit) / float64(weapon.ShotsFired)) * 100
kpm := weapon.KPM
weaponMetrics := FindWeaponMetric(gameMetrics.WeaponMetrics, weapon.Type)
if weaponMetrics == nil {
_, _ = fmt.Fprintf(GinWriter, "No weapon metrics specified for '"+gameTag+"', WType '"+weapon.Type+"'\n")
continue
}
accFactor := weaponMetrics.AccuracyFactor
kpmFactor := weaponMetrics.KpmFactor
acc /= accFactor
kpm /= kpmFactor
weapon.Accuracy = acc
weapon.KPM = kpm
top = append(top, weapon)
}
sumAcc := 0.0
sumKpm := 0.0
for _, w := range top {
sumAcc += w.Accuracy
sumKpm += w.KPM
}
accAvg := sumAcc / float64(len(top))
kpmAvg := sumKpm / float64(len(top))
score := math.Round(((accAvg*kpmAvg)/normalizeFactor)*100) / 100
return float32(score)
}

View File

@@ -1,7 +0,0 @@
package utils
import "github.com/gorilla/sessions"
var Store = sessions.NewCookieStore([]byte("f0q0qew0!)§(ds9713lsda231"))
const LoginSessionName = "session"