Compare commits
142 Commits
1515e29d5b
...
master
Author | SHA1 | Date | |
---|---|---|---|
ef20829b2e | |||
e716f5700c | |||
b327e08062 | |||
6ada023cea | |||
7389f70128 | |||
cc7c96311f | |||
d3d7c4b573 | |||
3d58ff2391 | |||
9cda542b01 | |||
e1d7b4e20b | |||
a4c6e9a905 | |||
c8e8104d5d | |||
de28ae027f | |||
a5033c0fb0 | |||
742edaa9e2 | |||
9712119238 | |||
f8a4924d56 | |||
fa3561ae58 | |||
548bd6984b | |||
27e1196130 | |||
173ef92c01 | |||
23e5ba432b | |||
2ead62ecce | |||
b18dd5ef82 | |||
c4758c6217 | |||
f9aa4e3c10 | |||
34c2d0d5e7 | |||
82fa877cad | |||
73683d66e8 | |||
ecaace5616 | |||
db018b35f9 | |||
45dd50ccba | |||
bcb2cd2f9c | |||
32e91e2601 | |||
35e5eadb05 | |||
651a738646 | |||
e43b9c9341 | |||
dfc85c5913 | |||
879cd7991b | |||
84c1a8f836 | |||
0f9bde2533 | |||
1ed9816b12 | |||
8ca5573b86 | |||
8c600daf86 | |||
bdef101bc0 | |||
d6c364ceb3 | |||
0c0164c2e8 | |||
53d856c7a7 | |||
bc31a94c98 | |||
0fac34edfb | |||
5979fd5b2c | |||
4616f7cc93 | |||
378c60cef8 | |||
af2bc29068 | |||
07d9335180 | |||
e737d31fd9 | |||
701d9b1929 | |||
e2e0cdc9b0 | |||
e65713a5b2 | |||
816ee07947 | |||
6db14a71af | |||
a649f468fc | |||
2f541f2346 | |||
bac655c1cf | |||
a2d033b56c | |||
76cc34c90a | |||
160fc79a07 | |||
59e1bf1ddf | |||
badae3aa0d | |||
11ca7c0ba6 | |||
09dddf7721 | |||
4fa155cafb | |||
1bb73c996b | |||
76682ab27f | |||
64fd2f3cd9 | |||
497c0692ca | |||
a08372d4d2 | |||
271e61510e | |||
4bf28e5298 | |||
7ff93f1344 | |||
a40ab9efdb | |||
bf7c10d195 | |||
17a99cce61 | |||
7dbb552dd2 | |||
7d1165b87b | |||
eaa8fb5225 | |||
ec0048042a | |||
62a3168190 | |||
79844c414b | |||
cd01657857 | |||
b68aed0284 | |||
d7ee87433f | |||
820d96f6b0 | |||
509390b38a | |||
78d624697c | |||
5b1c0784ec | |||
e115374589 | |||
09010d5867 | |||
867a556204 | |||
71e959805f | |||
fabde24734 | |||
123342b13b | |||
dd9a44d126 | |||
dc2de65245 | |||
a43e89a5a3 | |||
0db2967214 | |||
0dbc30d53c | |||
e4ba72725e | |||
144219f0aa | |||
72d0cbb7be | |||
1f75a9e64b | |||
2dac1759d9 | |||
5b516094a0 | |||
6a4f2b6869 | |||
5bdc59b4af | |||
5273498af1 | |||
fcd3b56f89 | |||
61976f6c47 | |||
9c0abdb46f | |||
f33497d002 | |||
94e55a6d4e | |||
afc2ec80e9 | |||
573ccc0478 | |||
fdf2d28f51 | |||
0115e6723a | |||
02f122328a | |||
77c543f88e | |||
50498976af | |||
bede9716f1 | |||
3074cd93d8 | |||
87c3b81ad4 | |||
20ff541ab4 | |||
e0c9646c98 | |||
d3a1382711 | |||
f8492ea1af | |||
10e9813bea | |||
192a03e9d1 | |||
08c639497d | |||
312cf820ab | |||
55b3a6f164 | |||
659548a544 | |||
c16019341f |
2
.gitignore
vendored
2
.gitignore
vendored
@ -2,4 +2,6 @@
|
|||||||
/uploads
|
/uploads
|
||||||
/keys
|
/keys
|
||||||
|
|
||||||
|
geoip.mmdb
|
||||||
|
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
12
.idea/Passport.iml
generated
12
.idea/Passport.iml
generated
@ -1,12 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<module type="WEB_MODULE" version="4">
|
|
||||||
<component name="Go" enabled="true" />
|
|
||||||
<component name="NewModuleRootManager">
|
|
||||||
<content url="file://$MODULE_DIR$" />
|
|
||||||
<orderEntry type="inheritedJdk" />
|
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
|
||||||
<orderEntry type="library" name="animate.css" level="application" />
|
|
||||||
<orderEntry type="library" name="tailwindcss" level="application" />
|
|
||||||
<orderEntry type="library" name="@tailwindcss/typography" level="application" />
|
|
||||||
</component>
|
|
||||||
</module>
|
|
59
.idea/codeStyles/Project.xml
generated
59
.idea/codeStyles/Project.xml
generated
@ -1,59 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<code_scheme name="Project" version="173">
|
|
||||||
<HTMLCodeStyleSettings>
|
|
||||||
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
|
||||||
</HTMLCodeStyleSettings>
|
|
||||||
<JSCodeStyleSettings version="0">
|
|
||||||
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
|
||||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
|
||||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
|
||||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
|
||||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
|
||||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
|
||||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
|
||||||
</JSCodeStyleSettings>
|
|
||||||
<TypeScriptCodeStyleSettings version="0">
|
|
||||||
<option name="USE_SEMICOLON_AFTER_STATEMENT" value="false" />
|
|
||||||
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
|
||||||
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
|
||||||
<option name="FORCE_QUOTE_STYlE" value="true" />
|
|
||||||
<option name="ENFORCE_TRAILING_COMMA" value="WhenMultiline" />
|
|
||||||
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
|
||||||
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
|
||||||
</TypeScriptCodeStyleSettings>
|
|
||||||
<VueCodeStyleSettings>
|
|
||||||
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
|
||||||
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
|
||||||
</VueCodeStyleSettings>
|
|
||||||
<codeStyleSettings language="HTML">
|
|
||||||
<option name="SOFT_MARGINS" value="120" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="INDENT_SIZE" value="2" />
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
||||||
<option name="TAB_SIZE" value="2" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="JavaScript">
|
|
||||||
<option name="SOFT_MARGINS" value="120" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="INDENT_SIZE" value="2" />
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
||||||
<option name="TAB_SIZE" value="2" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="TypeScript">
|
|
||||||
<option name="SOFT_MARGINS" value="120" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="INDENT_SIZE" value="2" />
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
||||||
<option name="TAB_SIZE" value="2" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
<codeStyleSettings language="Vue">
|
|
||||||
<option name="SOFT_MARGINS" value="120" />
|
|
||||||
<indentOptions>
|
|
||||||
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
|
||||||
</indentOptions>
|
|
||||||
</codeStyleSettings>
|
|
||||||
</code_scheme>
|
|
||||||
</component>
|
|
5
.idea/codeStyles/codeStyleConfig.xml
generated
5
.idea/codeStyles/codeStyleConfig.xml
generated
@ -1,5 +0,0 @@
|
|||||||
<component name="ProjectCodeStyleConfiguration">
|
|
||||||
<state>
|
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
|
||||||
</state>
|
|
||||||
</component>
|
|
43
.idea/dataSources.local.xml
generated
43
.idea/dataSources.local.xml
generated
@ -1,43 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="dataSourceStorageLocal" created-in="GO-243.21565.208">
|
|
||||||
<data-source name="hy_passport@localhost" uuid="74bcf3ef-a2b9-435b-b9e5-f32902a33b25">
|
|
||||||
<database-info product="PostgreSQL" version="16.4 (Homebrew)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.4" exact-driver-version="42.6">
|
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
|
||||||
</database-info>
|
|
||||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
|
||||||
<secret-storage>master_key</secret-storage>
|
|
||||||
<user-name>postgres</user-name>
|
|
||||||
<schema-mapping>
|
|
||||||
<introspection-scope>
|
|
||||||
<node negative="1">
|
|
||||||
<node kind="database">
|
|
||||||
<name qname="sn_interactive" />
|
|
||||||
<name qname="sn_messaging" />
|
|
||||||
</node>
|
|
||||||
<node kind="database">
|
|
||||||
<name qname="sn_paperclip" />
|
|
||||||
<name qname="sn_passport" />
|
|
||||||
<node kind="schema" qname="public" />
|
|
||||||
</node>
|
|
||||||
</node>
|
|
||||||
</introspection-scope>
|
|
||||||
</schema-mapping>
|
|
||||||
</data-source>
|
|
||||||
<data-source name="passport@id.solsynth.dev" uuid="723637bc-6ce3-4bbe-afb3-d88730c75a1b">
|
|
||||||
<database-info product="PostgreSQL" version="16.2 (Ubuntu 16.2-1.pgdg22.04+1)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.2" exact-driver-version="42.6">
|
|
||||||
<identifier-quote-string>"</identifier-quote-string>
|
|
||||||
</database-info>
|
|
||||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
|
||||||
<secret-storage>master_key</secret-storage>
|
|
||||||
<user-name>postgres</user-name>
|
|
||||||
<schema-mapping>
|
|
||||||
<introspection-scope>
|
|
||||||
<node kind="database" qname="@">
|
|
||||||
<node kind="schema" qname="@" />
|
|
||||||
</node>
|
|
||||||
</introspection-scope>
|
|
||||||
</schema-mapping>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
19
.idea/dataSources.xml
generated
19
.idea/dataSources.xml
generated
@ -1,19 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
|
||||||
<data-source source="LOCAL" name="hy_passport@localhost" uuid="74bcf3ef-a2b9-435b-b9e5-f32902a33b25">
|
|
||||||
<driver-ref>postgresql</driver-ref>
|
|
||||||
<synchronize>true</synchronize>
|
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:postgresql://localhost:5432/hy_passport</jdbc-url>
|
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
|
||||||
</data-source>
|
|
||||||
<data-source source="LOCAL" name="passport@id.solsynth.dev" uuid="723637bc-6ce3-4bbe-afb3-d88730c75a1b">
|
|
||||||
<driver-ref>postgresql</driver-ref>
|
|
||||||
<synchronize>true</synchronize>
|
|
||||||
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
|
|
||||||
<jdbc-url>jdbc:postgresql://id.solsynth.dev:5432/passport</jdbc-url>
|
|
||||||
<working-dir>$ProjectFileDir$</working-dir>
|
|
||||||
</data-source>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
6598
.idea/dataSources/723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml
generated
6598
.idea/dataSources/723637bc-6ce3-4bbe-afb3-d88730c75a1b.xml
generated
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
#n:passport
|
|
@ -1,2 +0,0 @@
|
|||||||
#n:public
|
|
||||||
!<md> [41831, 0, null, null, -2147483648, -2147483648]
|
|
10553
.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml
generated
10553
.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml
generated
File diff suppressed because it is too large
Load Diff
@ -1 +0,0 @@
|
|||||||
#n:hy_passport
|
|
@ -1,2 +0,0 @@
|
|||||||
#n:public
|
|
||||||
!<md> [10209, 0, null, null, -2147483648, -2147483648]
|
|
6
.idea/inspectionProfiles/Project_Default.xml
generated
6
.idea/inspectionProfiles/Project_Default.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<component name="InspectionProjectProfileManager">
|
|
||||||
<profile version="1.0">
|
|
||||||
<option name="myName" value="Project Default" />
|
|
||||||
<inspection_tool class="Eslint" enabled="true" level="WARNING" enabled_by_default="true" />
|
|
||||||
</profile>
|
|
||||||
</component>
|
|
6
.idea/jsLibraryMappings.xml
generated
6
.idea/jsLibraryMappings.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="JavaScriptLibraryMappings">
|
|
||||||
<file url="PROJECT" libraries="{@tailwindcss/typography, animate.css, tailwindcss}" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
8
.idea/modules.xml
generated
8
.idea/modules.xml
generated
@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="ProjectModuleManager">
|
|
||||||
<modules>
|
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/Passport.iml" filepath="$PROJECT_DIR$/.idea/Passport.iml" />
|
|
||||||
</modules>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
6
.idea/vcs.xml
generated
6
.idea/vcs.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="VcsDirectoryMappings">
|
|
||||||
<mapping directory="" vcs="Git" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
213
.idea/workspace.xml
generated
213
.idea/workspace.xml
generated
@ -1,213 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="AutoImportSettings">
|
|
||||||
<option name="autoReloadType" value="ALL" />
|
|
||||||
</component>
|
|
||||||
<component name="ChangeListManager">
|
|
||||||
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":bug: Fix group permission didn't extend properly">
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
|
||||||
</list>
|
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
|
||||||
<option name="HIGHLIGHT_NON_ACTIVE_CHANGELIST" value="false" />
|
|
||||||
<option name="LAST_RESOLUTION" value="IGNORE" />
|
|
||||||
</component>
|
|
||||||
<component name="FileTemplateManagerImpl">
|
|
||||||
<option name="RECENT_TEMPLATES">
|
|
||||||
<list>
|
|
||||||
<option value="Go File" />
|
|
||||||
</list>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="GOROOT" url="file:///opt/homebrew/opt/go/libexec" />
|
|
||||||
<component name="Git.Settings">
|
|
||||||
<option name="RECENT_BRANCH_BY_REPOSITORY">
|
|
||||||
<map>
|
|
||||||
<entry key="$PROJECT_DIR$" value="master" />
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
<option name="RECENT_GIT_ROOT_PATH" value="$PROJECT_DIR$" />
|
|
||||||
</component>
|
|
||||||
<component name="KubernetesApiPersistence">{}</component>
|
|
||||||
<component name="KubernetesApiProvider">{
|
|
||||||
"isMigrated": true
|
|
||||||
}</component>
|
|
||||||
<component name="ProblemsViewState">
|
|
||||||
<option name="selectedTabId" value="ProjectErrors" />
|
|
||||||
</component>
|
|
||||||
<component name="ProjectColorInfo">{
|
|
||||||
"customColor": "",
|
|
||||||
"associatedIndex": 6
|
|
||||||
}</component>
|
|
||||||
<component name="ProjectId" id="2fLXu43fjlLYVIGNrhGhOgBFq2O" />
|
|
||||||
<component name="ProjectViewState">
|
|
||||||
<option name="hideEmptyMiddlePackages" value="true" />
|
|
||||||
<option name="showLibraryContents" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="PropertiesComponent">{
|
|
||||||
"keyToString": {
|
|
||||||
"DefaultGoTemplateProperty": "Go File",
|
|
||||||
"Go Build.Backend.executor": "Debug",
|
|
||||||
"Go 构建.Backend.executor": "Run",
|
|
||||||
"RunOnceActivity.ShowReadmeOnStart": "true",
|
|
||||||
"RunOnceActivity.git.unshallow": "true",
|
|
||||||
"RunOnceActivity.go.formatter.settings.were.checked": "true",
|
|
||||||
"RunOnceActivity.go.migrated.go.modules.settings": "true",
|
|
||||||
"RunOnceActivity.go.modules.automatic.dependencies.download": "true",
|
|
||||||
"RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true",
|
|
||||||
"git-widget-placeholder": "master",
|
|
||||||
"go.import.settings.migrated": "true",
|
|
||||||
"go.sdk.automatically.set": "true",
|
|
||||||
"last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hypernet/Passport",
|
|
||||||
"node.js.detected.package.eslint": "true",
|
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
|
||||||
"nodejs_package_manager_path": "npm",
|
|
||||||
"run.code.analysis.last.selected.profile": "pProject Default",
|
|
||||||
"settings.editor.selected.configurable": "preferences.intentionPowerPack",
|
|
||||||
"ts.external.directory.path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web/node_modules/typescript/lib",
|
|
||||||
"vue.rearranger.settings.migration": "true"
|
|
||||||
},
|
|
||||||
"keyToStringList": {
|
|
||||||
"DatabaseDriversLRU": [
|
|
||||||
"postgresql"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}</component>
|
|
||||||
<component name="RecentsManager">
|
|
||||||
<key name="CopyFile.RECENT_KEYS">
|
|
||||||
<recent name="$PROJECT_DIR$/keys" />
|
|
||||||
<recent name="$PROJECT_DIR$/pkg/proto" />
|
|
||||||
<recent name="$PROJECT_DIR$/web/src/components/admin" />
|
|
||||||
<recent name="$PROJECT_DIR$/web/src/views" />
|
|
||||||
<recent name="$PROJECT_DIR$/pkg/internal/server/api" />
|
|
||||||
</key>
|
|
||||||
<key name="MoveFile.RECENT_KEYS">
|
|
||||||
<recent name="$PROJECT_DIR$/pkg/authkit" />
|
|
||||||
<recent name="$PROJECT_DIR$/web/src/views/flow" />
|
|
||||||
<recent name="$PROJECT_DIR$/pkg/internal/server/exts" />
|
|
||||||
<recent name="$PROJECT_DIR$/pkg/internal/server/api" />
|
|
||||||
<recent name="$PROJECT_DIR$/pkg/internal" />
|
|
||||||
</key>
|
|
||||||
</component>
|
|
||||||
<component name="RunAnythingCache">
|
|
||||||
<myKeys>
|
|
||||||
<visibility group="Grunt" flag="true" />
|
|
||||||
<visibility group="Gulp" flag="true" />
|
|
||||||
<visibility group="HTTP Requests" flag="true" />
|
|
||||||
<visibility group="HTTP 请求" flag="true" />
|
|
||||||
<visibility group="Recent projects" flag="true" />
|
|
||||||
<visibility group="Run configurations" flag="true" />
|
|
||||||
<visibility group="npm" flag="true" />
|
|
||||||
<visibility group="yarn" flag="true" />
|
|
||||||
</myKeys>
|
|
||||||
</component>
|
|
||||||
<component name="RunManager">
|
|
||||||
<configuration name="Backend" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
|
||||||
<module name="Passport" />
|
|
||||||
<working_directory value="$PROJECT_DIR$" />
|
|
||||||
<kind value="FILE" />
|
|
||||||
<package value="git.solsynth.dev/hydrogen/passport" />
|
|
||||||
<directory value="$PROJECT_DIR$" />
|
|
||||||
<filePath value="$PROJECT_DIR$/pkg/main.go" />
|
|
||||||
<output_directory value="$PROJECT_DIR$/dist" />
|
|
||||||
<method v="2" />
|
|
||||||
</configuration>
|
|
||||||
</component>
|
|
||||||
<component name="SharedIndexes">
|
|
||||||
<attachedChunks>
|
|
||||||
<set>
|
|
||||||
<option value="bundled-gosdk-d297c17c1fbd-b87a2f8923ed-org.jetbrains.plugins.go.sharedIndexes.bundled-GO-243.21565.208" />
|
|
||||||
<option value="bundled-js-predefined-d6986cc7102b-e768b9ed790e-JavaScript-GO-243.21565.208" />
|
|
||||||
</set>
|
|
||||||
</attachedChunks>
|
|
||||||
</component>
|
|
||||||
<component name="SpellCheckerSettings" RuntimeDictionaries="0" Folders="0" CustomDictionaries="0" DefaultDictionary="应用程序级" UseSingleDictionary="true" transferred="true" />
|
|
||||||
<component name="TypeScriptGeneratedFilesManager">
|
|
||||||
<option name="version" value="3" />
|
|
||||||
</component>
|
|
||||||
<component name="UnknownFeatures">
|
|
||||||
<option featureType="dependencySupport" implementationName="javascript:npm:prettier" />
|
|
||||||
<option featureType="dependencySupport" implementationName="executable:docker" />
|
|
||||||
<option featureType="dependencySupport" implementationName="javascript:npm:unocss" />
|
|
||||||
<option featureType="dependencySupport" implementationName="javascript:npm:vite" />
|
|
||||||
<option featureType="dependencySupport" implementationName="executable:kubectl" />
|
|
||||||
<option featureType="dependencySupport" implementationName="javascript:npm:vue" />
|
|
||||||
</component>
|
|
||||||
<component name="Vcs.Log.Tabs.Properties">
|
|
||||||
<option name="TAB_STATES">
|
|
||||||
<map>
|
|
||||||
<entry key="MAIN">
|
|
||||||
<value>
|
|
||||||
<State>
|
|
||||||
<option name="FILTERS">
|
|
||||||
<map>
|
|
||||||
<entry key="branch">
|
|
||||||
<value>
|
|
||||||
<list>
|
|
||||||
<option value="master" />
|
|
||||||
</list>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</State>
|
|
||||||
</value>
|
|
||||||
</entry>
|
|
||||||
</map>
|
|
||||||
</option>
|
|
||||||
</component>
|
|
||||||
<component name="VcsManagerConfiguration">
|
|
||||||
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
|
|
||||||
<MESSAGE value="Revert ":recycle: Move models.Account to sec.UserInfo" This reverts commit 8fbb7960" />
|
|
||||||
<MESSAGE value=":rewind: Revert ":recycle: Move models.Account to sec.UserInfo" for a better solution This reverts commit 8fbb7960" />
|
|
||||||
<MESSAGE value=":truck: Update package name from Hypdrogen to Hypernet" />
|
|
||||||
<MESSAGE value=":sparkles: Support users related rpc calls" />
|
|
||||||
<MESSAGE value=":technologist: Improve DX by extending authkit" />
|
|
||||||
<MESSAGE value=":sparkles: Add list relative method into authkit" />
|
|
||||||
<MESSAGE value=":sparkles: Realm operations now available in authkit" />
|
|
||||||
<MESSAGE value=":bug: Fix grpc namespace conflict" />
|
|
||||||
<MESSAGE value=":recycle: Replace nil context with context.Background in authkit" />
|
|
||||||
<MESSAGE value=":sparkles: Authkit support check user related permission" />
|
|
||||||
<MESSAGE value=":sparkles: Authkit can get user" />
|
|
||||||
<MESSAGE value=":bug: Fix auth cache" />
|
|
||||||
<MESSAGE value=":sparkles: Allow get realm by numeric auto increment id" />
|
|
||||||
<MESSAGE value=":sparkles: Allow access user info via numeric id" />
|
|
||||||
<MESSAGE value=":bug: Fix get user info query statement bug" />
|
|
||||||
<MESSAGE value=":boom: Remove deprecated subscription API" />
|
|
||||||
<MESSAGE value=":loud_sound: Verbose notifying logging" />
|
|
||||||
<MESSAGE value=":loud_sound: Verbose notifying check logging" />
|
|
||||||
<MESSAGE value=":bug: Fix notifiable is empty when user do not set" />
|
|
||||||
<MESSAGE value=":bug: Fix notification push batch emitted twice" />
|
|
||||||
<MESSAGE value=":sparkles: Support jwks.json according OIDC stranded" />
|
|
||||||
<MESSAGE value=":bug: Fix get user grpc method didn't extend group's permission" />
|
|
||||||
<MESSAGE value=":bug: Fix unable get user full perm node" />
|
|
||||||
<MESSAGE value=":loud_sound: Add verbose permission logging" />
|
|
||||||
<MESSAGE value=":bug: Fix group permission didn't extend properly" />
|
|
||||||
<option name="LAST_COMMIT_MESSAGE" value=":bug: Fix group permission didn't extend properly" />
|
|
||||||
<option name="GROUP_MULTIFILE_MERGE_BY_DIRECTORY" value="true" />
|
|
||||||
</component>
|
|
||||||
<component name="VgoProject">
|
|
||||||
<settings-migrated>true</settings-migrated>
|
|
||||||
</component>
|
|
||||||
<component name="XDebuggerManager">
|
|
||||||
<breakpoint-manager>
|
|
||||||
<breakpoints>
|
|
||||||
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
|
|
||||||
<url>file://$PROJECT_DIR$/pkg/internal/services/factors.go</url>
|
|
||||||
<line>85</line>
|
|
||||||
<option name="timeStamp" value="1" />
|
|
||||||
</line-breakpoint>
|
|
||||||
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
|
|
||||||
<url>file://$PROJECT_DIR$/pkg/internal/services/notifications.go</url>
|
|
||||||
<line>93</line>
|
|
||||||
<option name="timeStamp" value="3" />
|
|
||||||
</line-breakpoint>
|
|
||||||
<line-breakpoint enabled="true" type="DlvLineBreakpoint">
|
|
||||||
<url>file://$PROJECT_DIR$/pkg/internal/services/notifications.go</url>
|
|
||||||
<line>73</line>
|
|
||||||
<option name="timeStamp" value="4" />
|
|
||||||
</line-breakpoint>
|
|
||||||
</breakpoints>
|
|
||||||
</breakpoint-manager>
|
|
||||||
</component>
|
|
||||||
</project>
|
|
@ -9,6 +9,8 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -buildvcs -o /dist ./pkg/main
|
|||||||
FROM golang:alpine
|
FROM golang:alpine
|
||||||
|
|
||||||
COPY --from=passport-server /dist /passport/server
|
COPY --from=passport-server /dist /passport/server
|
||||||
|
COPY ./templates /templates
|
||||||
|
COPY ./locales /locales
|
||||||
|
|
||||||
EXPOSE 8444
|
EXPOSE 8444
|
||||||
|
|
||||||
|
61
go.mod
61
go.mod
@ -3,26 +3,28 @@ module git.solsynth.dev/hypernet/passport
|
|||||||
go 1.23.2
|
go 1.23.2
|
||||||
|
|
||||||
require (
|
require (
|
||||||
git.solsynth.dev/hypernet/nexus v0.0.0-20241123050605-25ab1371739b
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250330063116-4350d197f9c6
|
||||||
git.solsynth.dev/hypernet/pusher v0.0.0-20241026153052-cd2c326efa4e
|
git.solsynth.dev/hypernet/paperclip v0.0.0-20250310151112-1d866f317f47
|
||||||
github.com/dgraph-io/ristretto v0.2.0
|
git.solsynth.dev/hypernet/pusher v0.0.0-20250216145944-5fb769823a88
|
||||||
github.com/eko/gocache/lib/v4 v4.1.6
|
git.solsynth.dev/hypernet/wallet v0.0.0-20250323095812-468cd655f886
|
||||||
github.com/eko/gocache/store/ristretto/v4 v4.2.2
|
|
||||||
github.com/fatih/color v1.18.0
|
github.com/fatih/color v1.18.0
|
||||||
github.com/go-playground/validator/v10 v10.22.1
|
github.com/go-playground/validator/v10 v10.22.1
|
||||||
github.com/goccy/go-json v0.10.3
|
github.com/goccy/go-json v0.10.3
|
||||||
github.com/gofiber/fiber/v2 v2.52.5
|
github.com/gofiber/contrib/fiberzerolog v1.0.2
|
||||||
|
github.com/gofiber/fiber/v2 v2.52.6
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
github.com/json-iterator/go v1.1.12
|
github.com/json-iterator/go v1.1.12
|
||||||
|
github.com/oschwald/geoip2-golang v1.11.0
|
||||||
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/robfig/cron/v3 v3.0.1
|
github.com/robfig/cron/v3 v3.0.1
|
||||||
github.com/rs/zerolog v1.33.0
|
github.com/rs/zerolog v1.33.0
|
||||||
github.com/samber/lo v1.47.0
|
github.com/samber/lo v1.47.0
|
||||||
github.com/spf13/viper v1.19.0
|
github.com/spf13/viper v1.19.0
|
||||||
github.com/sujit-baniya/flash v0.1.8
|
github.com/sujit-baniya/flash v0.1.8
|
||||||
golang.org/x/crypto v0.28.0
|
golang.org/x/crypto v0.33.0
|
||||||
google.golang.org/grpc v1.67.1
|
google.golang.org/grpc v1.70.0
|
||||||
google.golang.org/protobuf v1.35.1
|
google.golang.org/protobuf v1.36.4
|
||||||
gorm.io/datatypes v1.2.4
|
gorm.io/datatypes v1.2.4
|
||||||
gorm.io/driver/postgres v1.5.9
|
gorm.io/driver/postgres v1.5.9
|
||||||
gorm.io/gorm v1.25.12
|
gorm.io/gorm v1.25.12
|
||||||
@ -32,15 +34,17 @@ require (
|
|||||||
filippo.io/edwards25519 v1.1.0 // indirect
|
filippo.io/edwards25519 v1.1.0 // indirect
|
||||||
github.com/andybalholm/brotli v1.1.1 // indirect
|
github.com/andybalholm/brotli v1.1.1 // indirect
|
||||||
github.com/beorn7/perks v1.0.1 // indirect
|
github.com/beorn7/perks v1.0.1 // indirect
|
||||||
|
github.com/boombuler/barcode v1.0.2 // indirect
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||||
|
github.com/eko/gocache/lib/v4 v4.2.0 // indirect
|
||||||
|
github.com/eko/gocache/store/redis/v4 v4.2.2 // indirect
|
||||||
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
github.com/fsnotify/fsnotify v1.8.0 // indirect
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
|
github.com/gabriel-vasile/mimetype v1.4.3 // 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-sql-driver/mysql v1.8.1 // indirect
|
github.com/go-sql-driver/mysql v1.8.1 // indirect
|
||||||
github.com/golang/mock v1.6.0 // indirect
|
github.com/golang/mock v1.6.0 // indirect
|
||||||
github.com/golang/protobuf v1.5.4 // indirect
|
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/jackc/pgpassfile v1.0.0 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
|
||||||
@ -48,26 +52,27 @@ require (
|
|||||||
github.com/jackc/puddle/v2 v2.2.2 // indirect
|
github.com/jackc/puddle/v2 v2.2.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
|
||||||
github.com/klauspost/compress v1.17.11 // indirect
|
github.com/klauspost/compress v1.18.0 // indirect
|
||||||
github.com/leodido/go-urn v1.4.0 // indirect
|
github.com/leodido/go-urn v1.4.0 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // 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/nats-io/nats.go v1.37.0 // indirect
|
github.com/nats-io/nats.go v1.37.0 // indirect
|
||||||
github.com/nats-io/nkeys v0.4.7 // indirect
|
github.com/nats-io/nkeys v0.4.7 // indirect
|
||||||
github.com/nats-io/nuid v1.0.1 // indirect
|
github.com/nats-io/nuid v1.0.1 // indirect
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.5.0 // indirect
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
github.com/prometheus/client_golang v1.19.0 // indirect
|
||||||
github.com/prometheus/client_golang v1.14.0 // indirect
|
github.com/prometheus/client_model v0.6.1 // indirect
|
||||||
github.com/prometheus/client_model v0.3.0 // indirect
|
github.com/prometheus/common v0.52.3 // indirect
|
||||||
github.com/prometheus/common v0.37.0 // indirect
|
github.com/prometheus/procfs v0.13.0 // indirect
|
||||||
github.com/prometheus/procfs v0.8.0 // indirect
|
github.com/redis/go-redis/v9 v9.7.3 // indirect
|
||||||
github.com/rivo/uniseg v0.4.7 // indirect
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
github.com/sagikazarmark/locafero v0.6.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
@ -76,19 +81,17 @@ require (
|
|||||||
github.com/spf13/cast v1.7.0 // indirect
|
github.com/spf13/cast v1.7.0 // indirect
|
||||||
github.com/spf13/pflag v1.0.5 // indirect
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/subosito/gotenv v1.6.0 // indirect
|
github.com/subosito/gotenv v1.6.0 // indirect
|
||||||
github.com/tinylib/msgp v1.2.4 // indirect
|
github.com/tinylib/msgp v1.2.5 // indirect
|
||||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||||
github.com/valyala/fasthttp v1.57.0 // indirect
|
github.com/valyala/fasthttp v1.59.0 // indirect
|
||||||
github.com/valyala/tcplisten v1.0.0 // indirect
|
go.uber.org/mock v0.4.0 // indirect
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 // indirect
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
|
||||||
golang.org/x/net v0.30.0 // indirect
|
golang.org/x/net v0.35.0 // indirect
|
||||||
golang.org/x/sync v0.8.0 // indirect
|
golang.org/x/sync v0.11.0 // indirect
|
||||||
golang.org/x/sys v0.26.0 // indirect
|
golang.org/x/sys v0.30.0 // indirect
|
||||||
golang.org/x/text v0.19.0 // indirect
|
golang.org/x/text v0.22.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 // indirect
|
||||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
gorm.io/driver/mysql v1.5.7 // indirect
|
gorm.io/driver/mysql v1.5.7 // indirect
|
||||||
|
574
go.sum
574
go.sum
@ -1,85 +1,46 @@
|
|||||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
|
||||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
|
||||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
|
||||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
|
||||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
|
||||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
|
||||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
|
||||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
|
||||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
|
||||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
|
||||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
|
||||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
|
||||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
|
||||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
|
||||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
|
||||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
|
||||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
|
||||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
|
||||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
|
||||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
|
||||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
|
||||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
|
||||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
|
||||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
|
||||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
|
||||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
|
||||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
|
||||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
|
||||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
|
||||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
|
||||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
|
||||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
|
||||||
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
|
||||||
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
|
||||||
git.solsynth.dev/hypernet/nexus v0.0.0-20241123050605-25ab1371739b h1:8yB9kMwEMY/nIbmDDxrhH5sTypgmK5PIIiIfP5QXx4s=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329072130-f113ae6cbaf7 h1:0OitkUQJ3hrobm71UHETLB9N6jTgm6jKTeGRJuBI/6E=
|
||||||
git.solsynth.dev/hypernet/nexus v0.0.0-20241123050605-25ab1371739b/go.mod h1:PhLCv2lsNoscPVJbkWnxwQnJ141lc4RIEkVffrHwl4s=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329072130-f113ae6cbaf7/go.mod h1:5tk62VQ1DcbR0EAN2jAOqYxHiegUPEC805JlfQ/G19I=
|
||||||
git.solsynth.dev/hypernet/pusher v0.0.0-20241026153052-cd2c326efa4e h1:DtHhMjgxS/spUt/KEdbRFtaVnepI6Vx8pbHdJaNH1hs=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329072729-4a08fd8f1c46 h1:oH2jq7ZG5cslCULUMWqv4dS/YNvd+Xcuv4rBPj0uGA8=
|
||||||
git.solsynth.dev/hypernet/pusher v0.0.0-20241026153052-cd2c326efa4e/go.mod h1:XHTqFU/vBe4JiuAjl87GUcL8+w/IizSNoqH6n3WkQFc=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329072729-4a08fd8f1c46/go.mod h1:5tk62VQ1DcbR0EAN2jAOqYxHiegUPEC805JlfQ/G19I=
|
||||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329075932-d5422ab5b04c h1:XgdTgJxSAQuCbiG15hN5pY6chzcz8sX3Onm2itS+Ufs=
|
||||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250329075932-d5422ab5b04c/go.mod h1:5tk62VQ1DcbR0EAN2jAOqYxHiegUPEC805JlfQ/G19I=
|
||||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250330063116-4350d197f9c6 h1:K7dYn7/rAXry3dSghFVd4aHOt2+8nTbhdav6DTW8sP8=
|
||||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
git.solsynth.dev/hypernet/nexus v0.0.0-20250330063116-4350d197f9c6/go.mod h1:5tk62VQ1DcbR0EAN2jAOqYxHiegUPEC805JlfQ/G19I=
|
||||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
git.solsynth.dev/hypernet/paperclip v0.0.0-20250310151112-1d866f317f47 h1:fvu+bNKPTNtQocssnKbEZ66MqR0iBfAxY3HwlqnmYyE=
|
||||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
git.solsynth.dev/hypernet/paperclip v0.0.0-20250310151112-1d866f317f47/go.mod h1:jvxq2qftz2v72x+24+cTFJdQKr9eHQTdk3KVR7cx36s=
|
||||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
git.solsynth.dev/hypernet/pusher v0.0.0-20250216145944-5fb769823a88 h1:2HEENe9KUrdaJeNBzx9lsuXQGyzWqCgnLTKQnr8xFr8=
|
||||||
|
git.solsynth.dev/hypernet/pusher v0.0.0-20250216145944-5fb769823a88/go.mod h1:ildzMtLagNsLK0Rkw4Hgk2TrrwqZnjwJIUx0MNZwcDY=
|
||||||
|
git.solsynth.dev/hypernet/wallet v0.0.0-20250323095812-468cd655f886 h1:rVssXF8jZ64ctAfzlCgIgF22NCT9VAPAVxrwlcItx3s=
|
||||||
|
git.solsynth.dev/hypernet/wallet v0.0.0-20250323095812-468cd655f886/go.mod h1:rmomNGQ6RBSp8TpZGA8tFr5M54AL2NADJ/1n0MfrIRM=
|
||||||
|
github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0=
|
||||||
|
github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||||
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
github.com/andybalholm/brotli v1.0.4/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=
|
||||||
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
github.com/andybalholm/brotli v1.1.1 h1:PR2pgnyFznKEugtsUo0xLdDop5SKXd5Qf5ysW+7XdTA=
|
||||||
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
github.com/andybalholm/brotli v1.1.1/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
|
||||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
|
||||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/boombuler/barcode v1.0.2 h1:79yrbttoZrLGkL/oOI8hBrUKucwOL0oOjUgEguGMcJ4=
|
||||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/boombuler/barcode v1.0.2/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
|
||||||
|
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
|
||||||
|
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
|
||||||
|
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
|
||||||
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
|
||||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
|
||||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
|
||||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
|
||||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
|
||||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
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/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/dgraph-io/ristretto v0.2.0 h1:XAfl+7cmoUDWW/2Lx8TGZQjjxIQ2Ley9DSf52dru4WE=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
|
||||||
github.com/dgraph-io/ristretto v0.2.0/go.mod h1:8uBHCU/PBV4Ag0CJrP47b9Ofby5dqWNh4FicAdoqFNU=
|
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
|
||||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y=
|
github.com/eko/gocache/lib/v4 v4.2.0 h1:MNykyi5Xw+5Wu3+PUrvtOCaKSZM1nUSVftbzmeC7Yuw=
|
||||||
github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw=
|
github.com/eko/gocache/lib/v4 v4.2.0/go.mod h1:7ViVmbU+CzDHzRpmB4SXKyyzyuJ8A3UW3/cszpcqB4M=
|
||||||
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
github.com/eko/gocache/store/redis/v4 v4.2.2 h1:Thw31fzGuH3WzJywsdbMivOmP550D6JS7GDHhvCJPA0=
|
||||||
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
github.com/eko/gocache/store/redis/v4 v4.2.2/go.mod h1:LaTxLKx9TG/YUEybQvPMij++D7PBTIJ4+pzvk0ykz0w=
|
||||||
github.com/eko/gocache/lib/v4 v4.1.6 h1:5WWIGISKhE7mfkyF+SJyWwqa4Dp2mkdX8QsZpnENqJI=
|
|
||||||
github.com/eko/gocache/lib/v4 v4.1.6/go.mod h1:HFxC8IiG2WeRotg09xEnPD72sCheJiTSr4Li5Ameg7g=
|
|
||||||
github.com/eko/gocache/store/ristretto/v4 v4.2.2 h1:lXFzoZ5ck6Gy6ON7f5DHSkNt122qN7KoroCVgVwF7oo=
|
|
||||||
github.com/eko/gocache/store/ristretto/v4 v4.2.2/go.mod h1:uIvBVJzqRepr5L0RsbkfQ2iYfbyos2fuji/s4yM+aUM=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
|
||||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
|
||||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
|
||||||
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
|
||||||
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
|
||||||
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
|
||||||
@ -88,17 +49,10 @@ github.com/fsnotify/fsnotify v1.8.0 h1:dAwr6QBTBZIkG8roQaJjGof0pp0EeF+tNV7YBP3F/
|
|||||||
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
github.com/fsnotify/fsnotify v1.8.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
|
||||||
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
|
||||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
|
||||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
|
||||||
github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0=
|
|
||||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
|
||||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
|
||||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
|
||||||
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=
|
||||||
@ -110,84 +64,31 @@ github.com/go-playground/validator/v10 v10.22.1/go.mod h1:dbuPbCMFw/DrkbEynArYaC
|
|||||||
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||||
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
|
||||||
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
|
||||||
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
github.com/goccy/go-json v0.10.3 h1:KZ5WoDbxAIgm2HNbYckL0se1fHD6rz5j4ywS6ebzDqA=
|
||||||
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
github.com/goccy/go-json v0.10.3/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
|
||||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||||
|
github.com/gofiber/contrib/fiberzerolog v1.0.2 h1:LMa/luarQVeINoRwZLHtLQYepLPDIwUNB5OmdZKk+s8=
|
||||||
|
github.com/gofiber/contrib/fiberzerolog v1.0.2/go.mod h1:aTPsgArSgxRWcUeJ/K6PiICz3mbQENR1QOR426QwOoQ=
|
||||||
github.com/gofiber/fiber/v2 v2.36.0/go.mod h1:tgCr+lierLwLoVHHO/jn3Niannv34WRkQETU8wiL9fQ=
|
github.com/gofiber/fiber/v2 v2.36.0/go.mod h1:tgCr+lierLwLoVHHO/jn3Niannv34WRkQETU8wiL9fQ=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5 h1:tWoP1MJQjGEe4GB5TUGOi7P2E0ZMMRx5ZTG4rT+yGMo=
|
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
|
||||||
github.com/gofiber/fiber/v2 v2.52.5/go.mod h1:KEOE+cXMhXG0zHc9d8+E38hoX+ZN7bhOtgeF2oT6jrQ=
|
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
|
||||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||||
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI=
|
||||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
|
||||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
|
||||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
|
||||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
|
||||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
|
||||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
|
||||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
|
||||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
|
||||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
|
||||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
|
||||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
|
||||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
|
||||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
|
||||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
|
||||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
|
||||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
|
||||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
|
||||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
|
||||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
|
||||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
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/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
|
||||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
|
||||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
|
||||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
|
||||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
|
||||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
|
||||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
|
||||||
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=
|
||||||
@ -200,37 +101,23 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
|
|||||||
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
|
||||||
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
|
||||||
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
|
||||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
|
||||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
|
||||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
|
||||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
|
||||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
|
||||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
|
||||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
|
||||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
|
||||||
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
github.com/klauspost/compress v1.15.0/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk=
|
||||||
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
github.com/klauspost/compress v1.15.9/go.mod h1:PhcZ0MbTNciWF3rruxRgKxI5NkcHHrHUDtV4Yw2GlzU=
|
||||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
|
||||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
|
||||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||||
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
|
||||||
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
|
||||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||||
|
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
|
||||||
|
github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
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-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
@ -239,8 +126,6 @@ github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6T
|
|||||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
github.com/mattn/go-sqlite3 v1.14.15 h1:vfoHhTN1af61xCRSWzFIWzx2YskyMTwHLrExkBOjvxI=
|
||||||
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
github.com/mattn/go-sqlite3 v1.14.15/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
|
||||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
|
||||||
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
github.com/microsoft/go-mssqldb v0.17.0 h1:Fto83dMZPnYv1Zwx5vHHxpNraeEaUlQ/hhHLgZiaenE=
|
||||||
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
github.com/microsoft/go-mssqldb v0.17.0/go.mod h1:OkoNGhGEs8EZqchVTtochlXruEhEOaO4S0d2sB5aeGQ=
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
@ -248,63 +133,47 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR
|
|||||||
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=
|
||||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
|
||||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
|
||||||
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
github.com/nats-io/nats.go v1.37.0 h1:07rauXbVnnJvv1gfIyghFEo6lUcYRY0WXc3x7x0vUxE=
|
||||||
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
github.com/nats-io/nats.go v1.37.0/go.mod h1:Ubdu4Nh9exXdSz0RVWRFBbRfrbSxOYd26oF0wkWclB8=
|
||||||
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
github.com/nats-io/nkeys v0.4.7 h1:RwNJbbIdYCoClSDNY7QVKZlyb/wfT6ugvFCiKy6vDvI=
|
||||||
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
github.com/nats-io/nkeys v0.4.7/go.mod h1:kqXRgRDPlGy7nGaEDMuYzmiJCIAAWDK0IMBtDmGD0nc=
|
||||||
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
github.com/nats-io/nuid v1.0.1 h1:5iA8DT8V7q8WK2EScv2padNa/rTESc1KdnPw4TC2paw=
|
||||||
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
github.com/nats-io/nuid v1.0.1/go.mod h1:19wcPz3Ph3q0Jbyiqsd0kePYG7A95tJPxeL+1OSON2c=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.5.0 h1:3wH1gpaekcgGuwzWdSu7JwJhH9Tk87k1ezt0i1p2/Is=
|
||||||
|
github.com/nicksnyder/go-i18n/v2 v2.5.0/go.mod h1:DrhgsSDZxoAfvVrBVLXoxZn/pN5TXqaDbq7ju94viiQ=
|
||||||
|
github.com/oschwald/geoip2-golang v1.11.0 h1:hNENhCn1Uyzhf9PTmquXENiWS6AlxAEnBII6r8krA3w=
|
||||||
|
github.com/oschwald/geoip2-golang v1.11.0/go.mod h1:P9zG+54KPEFOliZ29i7SeYZ/GM6tfEL+rgSn03hYuUo=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.0 h1:R8xBorY71s84yO06NgTmQvqvTvlS/bnYZrrWX1MElnU=
|
||||||
|
github.com/oschwald/maxminddb-golang v1.13.0/go.mod h1:BU0z8BfFVhi1LQaonTwwGQlsHUEu9pWNdMfmq4ztm0o=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c h1:dAMKvw0MlJT1GshSTtih8C2gDs04w8dReiOGXrGLNoY=
|
||||||
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
github.com/philhofer/fwd v1.1.3-0.20240916144458-20a13a1f6b7c/go.mod h1:RqIHx9QI14HlwKwm98g9Re5prTQ6LdeRQn+gXJFxsJM=
|
||||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||||
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/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
github.com/prometheus/client_golang v1.19.0 h1:ygXvpU1AoN1MhdzckN+PyD9QJOSD4x7kmXYlnfbA6JU=
|
||||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
github.com/prometheus/client_golang v1.19.0/go.mod h1:ZRM9uEAypZakd+q/x7+gmsvXdURP+DABIEIjnmDdp+k=
|
||||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
github.com/prometheus/common v0.52.3 h1:5f8uj6ZwHSscOGNdIQg6OiZv/ybiK2CO2q2drVZAQSA=
|
||||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
github.com/prometheus/common v0.52.3/go.mod h1:BrxBKv3FWBIGXw89Mg1AeBq7FSyRzXWI3l3e7W3RN5U=
|
||||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/procfs v0.13.0 h1:GqzLlQyfsPbaEHaQkO7tbDlriv/4o5Hudv6OXHGKX7o=
|
||||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/prometheus/procfs v0.13.0/go.mod h1:cd4PFCR54QLnGKPaKGA6l+cfuNXtht43ZKY6tow0Y1g=
|
||||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM=
|
||||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA=
|
||||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
|
||||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
|
||||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
|
||||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
|
||||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
|
||||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
|
||||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
|
||||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
|
||||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
|
||||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
|
||||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
|
||||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
|
||||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
|
||||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
github.com/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs=
|
||||||
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
|
||||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M=
|
||||||
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
|
github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA=
|
||||||
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
|
|
||||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||||
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
github.com/rs/zerolog v1.33.0 h1:1cU2KZkvPxNyfgEmhHAz/1A9Bz+llsdYzklWFzgp0r8=
|
||||||
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
github.com/rs/zerolog v1.33.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||||
@ -314,9 +183,6 @@ github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6g
|
|||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc=
|
||||||
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU=
|
||||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
|
||||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
|
||||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
@ -328,11 +194,7 @@ github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An
|
|||||||
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
github.com/spf13/viper v1.19.0 h1:RWq5SEjt8o25SROyN3z2OrDB9l7RPd3lwTWU8EcEdcI=
|
||||||
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
|
||||||
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.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
|
||||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
|
||||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
|
||||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
|
||||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
@ -340,329 +202,89 @@ github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8
|
|||||||
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
|
||||||
github.com/sujit-baniya/flash v0.1.8 h1:BwcrybCatPU30VMA9IBA5q3ZE0VSr5c7qTqwZrSvyRI=
|
github.com/sujit-baniya/flash v0.1.8 h1:BwcrybCatPU30VMA9IBA5q3ZE0VSr5c7qTqwZrSvyRI=
|
||||||
github.com/sujit-baniya/flash v0.1.8/go.mod h1:kmlAIkLDMlLshEeeE6fETEW8kSOopKN5WA3KXLmS/U0=
|
github.com/sujit-baniya/flash v0.1.8/go.mod h1:kmlAIkLDMlLshEeeE6fETEW8kSOopKN5WA3KXLmS/U0=
|
||||||
github.com/tinylib/msgp v1.2.4 h1:yLFeUGostXXSGW5vxfT5dXG/qzkn4schv2I7at5+hVU=
|
github.com/tinylib/msgp v1.2.5 h1:WeQg1whrXRFiZusidTQqzETkRpGjFjcIhW6uqWH09po=
|
||||||
github.com/tinylib/msgp v1.2.4/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
github.com/tinylib/msgp v1.2.5/go.mod h1:ykjzy2wzgrlvpDCRc4LA8UXy6D8bzMSuAF3WD57Gok0=
|
||||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||||
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
|
||||||
github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
|
github.com/valyala/fasthttp v1.38.0/go.mod h1:t/G+3rLek+CyY9bnIE+YlMRddxVAAGjhxndDB4i4C0I=
|
||||||
github.com/valyala/fasthttp v1.57.0 h1:Xw8SjWGEP/+wAAgyy5XTvgrWlOD1+TxbbvNADYCm1Tg=
|
github.com/valyala/fasthttp v1.59.0 h1:Qu0qYHfXvPk1mSLNqcFtEk6DpxgA26hy6bmydotDpRI=
|
||||||
github.com/valyala/fasthttp v1.57.0/go.mod h1:h6ZBaPRlzpZ6O3H5t2gEk1Qi33+TmLvfwgLLp0t9CpE=
|
github.com/valyala/fasthttp v1.59.0/go.mod h1:GTxNb9Bc6r2a9D0TWNSPwDz78UxnTGBViY3xZNEqyYU=
|
||||||
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
|
|
||||||
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5 h1:5gO0H1iULLWGhs2H5tbAHIZTV8/cYafcFOr9znI5mJU=
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.3.5/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
|
|
||||||
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
github.com/xyproto/randomstring v1.0.5 h1:YtlWPoRdgMu3NZtP45drfy1GKoojuR7hmRcnhZqKjWU=
|
||||||
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
github.com/xyproto/randomstring v1.0.5/go.mod h1:rgmS5DeNXLivK7YprL0pY+lTuhNQW3iGxZ18UQApw/E=
|
||||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U=
|
||||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg=
|
||||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M=
|
||||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8=
|
||||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4=
|
||||||
|
go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU=
|
||||||
|
go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ=
|
||||||
|
go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM=
|
||||||
|
go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8=
|
||||||
|
go.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=
|
||||||
|
go.uber.org/mock v0.4.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
|
||||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
|
||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
|
||||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
|
||||||
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
golang.org/x/crypto v0.0.0-20220214200702-86341886e292/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=
|
||||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
golang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=
|
||||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
|
||||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
|
||||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
|
||||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
|
||||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
|
||||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
|
||||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
|
||||||
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
|
||||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
|
||||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
|
||||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
|
||||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
|
||||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
|
||||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
|
||||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
|
||||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
|
||||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
|
||||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
|
||||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
|
||||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
|
||||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
|
||||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
|
||||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
|
||||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
|
||||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
|
||||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
|
||||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
|
||||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
|
||||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
golang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=
|
||||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
golang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
|
||||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
|
||||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=
|
||||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
|
||||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
|
||||||
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
golang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=
|
||||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
|
||||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
|
||||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||||
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
|
golang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=
|
||||||
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=
|
||||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
|
||||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
|
||||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
|
||||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
|
||||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
|
||||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
|
||||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
|
||||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
|
||||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
|
||||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
|
||||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
|
||||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287 h1:J1H9f+LEdWAfHcez/4cvaVBox7cOYT+IU6rgqj5x++8=
|
||||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250127172529-29210b9bc287/go.mod h1:8BS3B93F/U1juMFq9+EDk+qOT5CO1R9IzXxG3PTqiRk=
|
||||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ=
|
||||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw=
|
||||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
google.golang.org/protobuf v1.36.4 h1:6A3ZDJHn/eNqc1i+IdefRzy/9PokBTPvcqMySR7NNIM=
|
||||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
google.golang.org/protobuf v1.36.4/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
|
||||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
|
||||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
|
||||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
|
||||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
|
||||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
|
||||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
|
||||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
|
||||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
|
||||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
|
||||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
|
||||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
|
||||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
|
||||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
|
||||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
|
||||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
|
||||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
|
||||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38 h1:zciRKQ4kBpFgpfC5QQCVtnnNAcLIqweL7plyZRQHVpI=
|
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20241021214115-324edc3d5d38/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
|
|
||||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
|
||||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
|
||||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
|
||||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
|
||||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
|
||||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
|
||||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
|
||||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
|
||||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
|
||||||
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
|
|
||||||
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
|
||||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
|
||||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
|
||||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
|
||||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
|
||||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
|
||||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
|
||||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
|
||||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
|
||||||
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
|
||||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
|
||||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
|
||||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
|
||||||
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=
|
||||||
@ -679,13 +301,3 @@ gorm.io/driver/sqlserver v1.4.1/go.mod h1:DJ4P+MeZbc5rvY58PnmN1Lnyvb5gw5NPzGshHD
|
|||||||
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8=
|
||||||
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8=
|
||||||
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ=
|
||||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
|
||||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
|
||||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
|
||||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
|
||||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
||||||
|
18
locales/en-US.json
Normal file
18
locales/en-US.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"subjectLoginOneTimePassword": "Login verification code",
|
||||||
|
"shortBodyLoginOneTimePassword": "%s is your login verification code. It will expires in 30 minutes.",
|
||||||
|
"subjectConfirmRegistration": "Confirm your registration",
|
||||||
|
"subjectResetPassword": "Reset your password",
|
||||||
|
"subjectDeleteAccount": "Confirm your account deletion",
|
||||||
|
"subjectLoginAlert": "Login alert",
|
||||||
|
"shortBodyLoginAlert": "Your account got logged in from %s. If it's not your device, please deal with it immediately.",
|
||||||
|
"subjectAbuseReportUpdated": "Abuse report status has been changed",
|
||||||
|
"shortBodyAbuseReportUpdated": "Report #%d has been changed to %s. Moderator message: %s",
|
||||||
|
"subtitlePunishment": "Case #%d Moderated by %s",
|
||||||
|
"subjectPunishmentCreated": "You have been punished",
|
||||||
|
"shortBodyPunishmentCreated": "You have been punished for %s. Learn more inside the app.",
|
||||||
|
"subjectPunishmentUpdated": "Your punishment has been updated",
|
||||||
|
"shortBodyPunishmentUpdated": "Your punishment #%s has been updated. Learn more inside the app.",
|
||||||
|
"subjectPunishmentDeleted": "Your punishment has been revoked",
|
||||||
|
"shortBodyPunishmentDeleted": "Your punishment #%s has been revoked."
|
||||||
|
}
|
18
locales/zh-CN.json
Normal file
18
locales/zh-CN.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"subjectLoginOneTimePassword": "您的验证码",
|
||||||
|
"shortBodyLoginOneTimePassword": "%s 是您的登录验证码,它将在 30 分钟后过期。",
|
||||||
|
"subjectConfirmRegistration": "确认您的注册",
|
||||||
|
"subjectResetPassword": "重置您的密码",
|
||||||
|
"subjectDeleteAccount": "确认您的帐户删除",
|
||||||
|
"subjectLoginAlert": "登陆提醒",
|
||||||
|
"shortBodyLoginAlert": "您的帐户在 %s 登录,若它不是你的设备,请立即处理。",
|
||||||
|
"subjectAbuseReportUpdated": "举报状态已更新",
|
||||||
|
"shortBodyAbuseReportUpdated": "举报 #%d 已更新为 %s。管理员回复:%s",
|
||||||
|
"subtitlePunishment": "案件 #%d 由 %s 处理",
|
||||||
|
"subjectPunishmentCreated": "你收到了一份处分",
|
||||||
|
"shortBodyPunishmentCreated": "你因为 %s 而被处分,详情请在应用内查看。",
|
||||||
|
"subjectPunishmentUpdated": "你的处分已更新",
|
||||||
|
"shortBodyPunishmentUpdated": "你的处分 #%s 已更新。详情请在应用内查看。",
|
||||||
|
"subjectPunishmentDeleted": "你的处分已撤销",
|
||||||
|
"shortBodyPunishmentDeleted": "你的处分 #%s 已撤销。"
|
||||||
|
}
|
@ -3,13 +3,14 @@ package authkit
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/proto"
|
"git.solsynth.dev/hypernet/passport/pkg/proto"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func AddEvent(nx *nex.Conn, userId uint, action, target, ip, ua string) error {
|
func AddEvent(nx *nex.Conn, userId uint, action string, meta map[string]any, ip, ua string) error {
|
||||||
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to get auth service client: %v", err)
|
return fmt.Errorf("failed to get auth service client: %v", err)
|
||||||
@ -17,14 +18,14 @@ func AddEvent(nx *nex.Conn, userId uint, action, target, ip, ua string) error {
|
|||||||
_, err = proto.NewAuditServiceClient(conn).RecordEvent(context.Background(), &proto.RecordEventRequest{
|
_, err = proto.NewAuditServiceClient(conn).RecordEvent(context.Background(), &proto.RecordEventRequest{
|
||||||
UserId: uint64(userId),
|
UserId: uint64(userId),
|
||||||
Action: action,
|
Action: action,
|
||||||
Target: target,
|
Metadata: nex.EncodeMap(meta),
|
||||||
Ip: ip,
|
Ip: ip,
|
||||||
UserAgent: ua,
|
UserAgent: ua,
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func AddEventExt(nx *nex.Conn, action, target string, c *fiber.Ctx) error {
|
func AddEventExt(nx *nex.Conn, action string, meta map[string]any, c *fiber.Ctx) error {
|
||||||
user, ok := c.Locals("nex_user").(*sec.UserInfo)
|
user, ok := c.Locals("nex_user").(*sec.UserInfo)
|
||||||
if !ok {
|
if !ok {
|
||||||
return fmt.Errorf("failed to get user info, make sure you call this method behind the ContextMiddleware")
|
return fmt.Errorf("failed to get user info, make sure you call this method behind the ContextMiddleware")
|
||||||
@ -37,7 +38,7 @@ func AddEventExt(nx *nex.Conn, action, target string, c *fiber.Ctx) error {
|
|||||||
_, err = proto.NewAuditServiceClient(conn).RecordEvent(context.Background(), &proto.RecordEventRequest{
|
_, err = proto.NewAuditServiceClient(conn).RecordEvent(context.Background(), &proto.RecordEventRequest{
|
||||||
UserId: uint64(user.ID),
|
UserId: uint64(user.ID),
|
||||||
Action: action,
|
Action: action,
|
||||||
Target: target,
|
Metadata: nex.EncodeMap(meta),
|
||||||
Ip: c.IP(),
|
Ip: c.IP(),
|
||||||
UserAgent: c.Get(fiber.HeaderUserAgent),
|
UserAgent: c.Get(fiber.HeaderUserAgent),
|
||||||
})
|
})
|
||||||
|
@ -2,10 +2,11 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -16,12 +17,12 @@ type Account struct {
|
|||||||
|
|
||||||
Name string `json:"name" gorm:"uniqueIndex"`
|
Name string `json:"name" gorm:"uniqueIndex"`
|
||||||
Nick string `json:"nick"`
|
Nick string `json:"nick"`
|
||||||
Description string `json:"description"`
|
|
||||||
Avatar *string `json:"avatar"`
|
Avatar *string `json:"avatar"`
|
||||||
Banner *string `json:"banner"`
|
Banner *string `json:"banner"`
|
||||||
ConfirmedAt *time.Time `json:"confirmed_at"`
|
ConfirmedAt *time.Time `json:"confirmed_at"`
|
||||||
SuspendedAt *time.Time `json:"suspended_at"`
|
SuspendedAt *time.Time `json:"suspended_at"`
|
||||||
PermNodes datatypes.JSONMap `json:"perm_nodes"`
|
PermNodes datatypes.JSONMap `json:"perm_nodes"`
|
||||||
|
Language string `json:"language"`
|
||||||
|
|
||||||
AutomatedBy *Account `json:"automated_by" gorm:"foreignKey:AutomatedID"`
|
AutomatedBy *Account `json:"automated_by" gorm:"foreignKey:AutomatedID"`
|
||||||
AutomatedID *uint `json:"automated_id"`
|
AutomatedID *uint `json:"automated_id"`
|
||||||
@ -37,6 +38,11 @@ type Account struct {
|
|||||||
Factors []AuthFactor `json:"factors,omitempty"`
|
Factors []AuthFactor `json:"factors,omitempty"`
|
||||||
|
|
||||||
Relations []AccountRelationship `json:"relations,omitempty" gorm:"foreignKey:AccountID"`
|
Relations []AccountRelationship `json:"relations,omitempty" gorm:"foreignKey:AccountID"`
|
||||||
|
|
||||||
|
Punishments []Punishment `json:"punishments,omitempty"`
|
||||||
|
|
||||||
|
// Keep this for backward compability
|
||||||
|
Description string `json:"description" gorm:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Account) GetAvatar() *string {
|
func (v Account) GetAvatar() *string {
|
||||||
@ -64,6 +70,7 @@ func (v Account) EncodeToUserInfo() *proto.UserInfo {
|
|||||||
return &proto.UserInfo{
|
return &proto.UserInfo{
|
||||||
Id: uint64(v.ID),
|
Id: uint64(v.ID),
|
||||||
Name: v.Name,
|
Name: v.Name,
|
||||||
|
Language: v.Language,
|
||||||
PermNodes: nex.EncodeMap(v.PermNodes),
|
PermNodes: nex.EncodeMap(v.PermNodes),
|
||||||
Metadata: nex.EncodeMap(v),
|
Metadata: nex.EncodeMap(v),
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,9 @@ type AuditRecord struct {
|
|||||||
|
|
||||||
Action string `json:"action"`
|
Action string `json:"action"`
|
||||||
Metadata datatypes.JSONMap `json:"metadata"`
|
Metadata datatypes.JSONMap `json:"metadata"`
|
||||||
|
Location *string `json:"location"`
|
||||||
|
CoordinateX *float64 `json:"coordinate_x"`
|
||||||
|
CoordinateY *float64 `json:"coordinate_y"`
|
||||||
UserAgent string `json:"user_agent"`
|
UserAgent string `json:"user_agent"`
|
||||||
IpAddress string `json:"ip_address"`
|
IpAddress string `json:"ip_address"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type AuthConfig struct {
|
type AuthConfig struct {
|
||||||
|
AlwaysRisky bool `json:"always_risky"`
|
||||||
MaximumAuthSteps int `json:"maximum_auth_steps" validate:"required,min=1,max=99"`
|
MaximumAuthSteps int `json:"maximum_auth_steps" validate:"required,min=1,max=99"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -16,6 +17,8 @@ type AuthFactorType = int8
|
|||||||
const (
|
const (
|
||||||
PasswordAuthFactor = AuthFactorType(iota)
|
PasswordAuthFactor = AuthFactorType(iota)
|
||||||
EmailPasswordFactor
|
EmailPasswordFactor
|
||||||
|
TimeOtpFactor
|
||||||
|
InAppNotifyFactor
|
||||||
)
|
)
|
||||||
|
|
||||||
type AuthFactor struct {
|
type AuthFactor struct {
|
||||||
@ -23,7 +26,7 @@ type AuthFactor struct {
|
|||||||
|
|
||||||
Type int8 `json:"type"`
|
Type int8 `json:"type"`
|
||||||
Secret string `json:"-"`
|
Secret string `json:"-"`
|
||||||
Config JSONMap `json:"config"`
|
Config datatypes.JSONMap `json:"config"`
|
||||||
|
|
||||||
Account Account `json:"account"`
|
Account Account `json:"account"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
@ -32,7 +35,9 @@ type AuthFactor struct {
|
|||||||
type AuthTicket struct {
|
type AuthTicket struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Location string `json:"location"`
|
Location *string `json:"location"`
|
||||||
|
CoordinateX *float64 `json:"coordinate_x"`
|
||||||
|
CoordinateY *float64 `json:"coordinate_y"`
|
||||||
IpAddress string `json:"ip_address"`
|
IpAddress string `json:"ip_address"`
|
||||||
UserAgent string `json:"user_agent"`
|
UserAgent string `json:"user_agent"`
|
||||||
StepRemain int `json:"step_remain"`
|
StepRemain int `json:"step_remain"`
|
||||||
|
@ -7,5 +7,6 @@ type Badge struct {
|
|||||||
|
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Metadata datatypes.JSONMap `json:"metadata"`
|
Metadata datatypes.JSONMap `json:"metadata"`
|
||||||
|
IsActive bool `json:"is_active"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
}
|
}
|
||||||
|
21
pkg/authkit/models/check_in.go
Normal file
21
pkg/authkit/models/check_in.go
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "gorm.io/datatypes"
|
||||||
|
|
||||||
|
type CheckInRecord struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
ResultTier int `json:"result_tier"`
|
||||||
|
ResultExperience int `json:"result_experience"`
|
||||||
|
ResultCoin float64 `json:"result_coin"`
|
||||||
|
CurrentStreak int `json:"current_streak"`
|
||||||
|
|
||||||
|
// The result modifiers are some random tips that will show up in the client;
|
||||||
|
// This field is to use to make sure the tips will be the same when the client is reloaded.
|
||||||
|
// For now, this modifier slice will contain four random numbers from 0 to 1024.
|
||||||
|
// Client should mod this modifier by the length of total available tips.
|
||||||
|
ResultModifiers datatypes.JSONSlice[int] `json:"result_modifiers"`
|
||||||
|
|
||||||
|
Account Account `json:"account"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
@ -1,11 +1,15 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
|
import "gorm.io/datatypes"
|
||||||
|
|
||||||
type ActionEvent struct {
|
type ActionEvent struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
Target string `json:"target"`
|
Metadata datatypes.JSONMap `json:"metadata"`
|
||||||
Location string `json:"location"`
|
Location *string `json:"location"`
|
||||||
|
CoordinateX *float64 `json:"coordinate_x"`
|
||||||
|
CoordinateY *float64 `json:"coordinate_y"`
|
||||||
IpAddress string `json:"ip_address"`
|
IpAddress string `json:"ip_address"`
|
||||||
UserAgent string `json:"user_agent"`
|
UserAgent string `json:"user_agent"`
|
||||||
|
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Notification struct {
|
type Notification struct {
|
||||||
|
@ -2,6 +2,8 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/datatypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
type AccountProfile struct {
|
type AccountProfile struct {
|
||||||
@ -9,8 +11,21 @@ type AccountProfile struct {
|
|||||||
|
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
TimeZone string `json:"time_zone"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
Pronouns string `json:"pronouns"`
|
||||||
|
Gender string `json:"gender"`
|
||||||
|
Links datatypes.JSONMap `json:"links"`
|
||||||
Experience uint64 `json:"experience"`
|
Experience uint64 `json:"experience"`
|
||||||
LastSeenAt *time.Time `json:"last_seen_at"`
|
LastSeenAt *time.Time `json:"last_seen_at"`
|
||||||
Birthday *time.Time `json:"birthday"`
|
Birthday *time.Time `json:"birthday"`
|
||||||
AccountID uint `json:"account_id"`
|
AccountID uint `json:"account_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type AccountPage struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
Content string `json:"content"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
||||||
|
44
pkg/authkit/models/programs.go
Normal file
44
pkg/authkit/models/programs.go
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ProgramPrice struct {
|
||||||
|
Currency string `json:"currency"`
|
||||||
|
Amount float64 `json:"amount"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProgramBadge struct {
|
||||||
|
Type string `json:"type"`
|
||||||
|
Metadata map[string]any `json:"metadata"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProgramGroup struct {
|
||||||
|
ID uint `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Program struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Alias string `json:"alias" gorm:"uniqueIndex"`
|
||||||
|
ExpRequirement int64 `json:"exp_requirement"`
|
||||||
|
Price datatypes.JSONType[ProgramPrice] `json:"price"`
|
||||||
|
Badge datatypes.JSONType[ProgramBadge] `json:"badge"`
|
||||||
|
Group datatypes.JSONType[ProgramGroup] `json:"group"`
|
||||||
|
Appearance datatypes.JSONMap `json:"appearance"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProgramMember struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
LastPaid *time.Time `json:"last_paid"`
|
||||||
|
Account Account `json:"account"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
Program Program `json:"program"`
|
||||||
|
ProgramID uint `json:"program_id"`
|
||||||
|
}
|
26
pkg/authkit/models/punishments.go
Normal file
26
pkg/authkit/models/punishments.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
PunishmentTypeStrike = iota
|
||||||
|
PunishmentTypeLimited
|
||||||
|
PunishmentTypeDisabled
|
||||||
|
)
|
||||||
|
|
||||||
|
type Punishment struct {
|
||||||
|
BaseModel
|
||||||
|
|
||||||
|
Reason string `json:"reason"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
PermNodes datatypes.JSONMap `json:"perm_nodes"`
|
||||||
|
ExpiredAt *time.Time `json:"expired_at"`
|
||||||
|
Account Account `json:"account"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
Moderator *Account `json:"moderator"`
|
||||||
|
ModeratorID *uint `json:"moderator_id"`
|
||||||
|
}
|
@ -6,6 +6,12 @@ import (
|
|||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
RealmPopularityMemberFactor = 5
|
||||||
|
RealmPopularityPostFactor = 10
|
||||||
|
RealmPopularityChatFactor = 2
|
||||||
|
)
|
||||||
|
|
||||||
type Realm struct {
|
type Realm struct {
|
||||||
BaseModel
|
BaseModel
|
||||||
|
|
||||||
@ -15,6 +21,7 @@ type Realm struct {
|
|||||||
Members []RealmMember `json:"members"`
|
Members []RealmMember `json:"members"`
|
||||||
Avatar *string `json:"avatar"`
|
Avatar *string `json:"avatar"`
|
||||||
Banner *string `json:"banner"`
|
Banner *string `json:"banner"`
|
||||||
|
Popularity int `json:"popularity"`
|
||||||
AccessPolicy datatypes.JSONMap `json:"access_policy"`
|
AccessPolicy datatypes.JSONMap `json:"access_policy"`
|
||||||
IsPublic bool `json:"is_public"`
|
IsPublic bool `json:"is_public"`
|
||||||
IsCommunity bool `json:"is_community"`
|
IsCommunity bool `json:"is_community"`
|
||||||
|
@ -1,10 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
type SignRecord struct {
|
|
||||||
BaseModel
|
|
||||||
|
|
||||||
ResultTier int `json:"result_tier"`
|
|
||||||
ResultExperience int `json:"result_experience"`
|
|
||||||
Account Account `json:"account"`
|
|
||||||
AccountID uint `json:"account_id"`
|
|
||||||
}
|
|
@ -18,4 +18,5 @@ type MagicToken struct {
|
|||||||
Type int8 `json:"type"`
|
Type int8 `json:"type"`
|
||||||
AccountID *uint `json:"account_id"`
|
AccountID *uint `json:"account_id"`
|
||||||
ExpiredAt *time.Time `json:"expired_at"`
|
ExpiredAt *time.Time `json:"expired_at"`
|
||||||
|
LastNotifiedAt *time.Time `json:"last_notified_at"`
|
||||||
}
|
}
|
||||||
|
65
pkg/authkit/third_client.go
Normal file
65
pkg/authkit/third_client.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
package authkit
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/proto"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
)
|
||||||
|
|
||||||
|
func GetThirdClient(nx *nex.Conn, id uint, secret *string) (*models.ThirdClient, error) {
|
||||||
|
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get auth service client: %v", err)
|
||||||
|
}
|
||||||
|
resp, err := proto.NewThirdClientServiceClient(conn).
|
||||||
|
GetThirdClient(context.Background(), &proto.GetThirdClientRequest{
|
||||||
|
Id: lo.ToPtr(uint64(id)),
|
||||||
|
Secret: secret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.ThirdClient{
|
||||||
|
Alias: resp.GetInfo().GetAlias(),
|
||||||
|
Name: resp.GetInfo().GetName(),
|
||||||
|
Description: resp.GetInfo().GetDescription(),
|
||||||
|
IsDraft: resp.GetInfo().GetIsDraft(),
|
||||||
|
AccountID: lo.TernaryF(resp.GetInfo().AccountId != nil, func() *uint {
|
||||||
|
return lo.ToPtr(uint(resp.GetInfo().GetAccountId()))
|
||||||
|
}, func() *uint {
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetThirdClientByAlias(nx *nex.Conn, alias string, secret *string) (*models.ThirdClient, error) {
|
||||||
|
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get auth service client: %v", err)
|
||||||
|
}
|
||||||
|
resp, err := proto.NewThirdClientServiceClient(conn).
|
||||||
|
GetThirdClient(context.Background(), &proto.GetThirdClientRequest{
|
||||||
|
Alias: &alias,
|
||||||
|
Secret: secret,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return &models.ThirdClient{
|
||||||
|
Alias: resp.GetInfo().GetAlias(),
|
||||||
|
Name: resp.GetInfo().GetName(),
|
||||||
|
Description: resp.GetInfo().GetDescription(),
|
||||||
|
IsDraft: resp.GetInfo().GetIsDraft(),
|
||||||
|
AccountID: lo.TernaryF(resp.GetInfo().AccountId != nil, func() *uint {
|
||||||
|
return lo.ToPtr(uint(resp.GetInfo().GetAccountId()))
|
||||||
|
}, func() *uint {
|
||||||
|
return nil
|
||||||
|
}),
|
||||||
|
}, nil
|
||||||
|
}
|
@ -2,7 +2,10 @@ package authkit
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
@ -10,6 +13,14 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func GetUser(nx *nex.Conn, userId uint) (models.Account, error) {
|
func GetUser(nx *nex.Conn, userId uint) (models.Account, error) {
|
||||||
|
cacheConn, err := cachekit.NewConn(nx, 3*time.Second)
|
||||||
|
if err == nil {
|
||||||
|
key := cachekit.FKey(cachekit.DAAttachment, userId)
|
||||||
|
if user, err := cachekit.Get[models.Account](cacheConn, key); err == nil {
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Account{}, err
|
return models.Account{}, err
|
||||||
@ -26,6 +37,14 @@ func GetUser(nx *nex.Conn, userId uint) (models.Account, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func GetUserByName(nx *nex.Conn, name string) (models.Account, error) {
|
func GetUserByName(nx *nex.Conn, name string) (models.Account, error) {
|
||||||
|
cacheConn, err := cachekit.NewConn(nx, 3*time.Second)
|
||||||
|
if err == nil {
|
||||||
|
key := cachekit.FKey(cachekit.DAAttachment, name)
|
||||||
|
if user, err := cachekit.Get[models.Account](cacheConn, key); err == nil {
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return models.Account{}, err
|
return models.Account{}, err
|
||||||
@ -41,24 +60,59 @@ func GetUserByName(nx *nex.Conn, name string) (models.Account, error) {
|
|||||||
}), nil
|
}), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListUser(nx *nex.Conn, userId []uint) ([]models.Account, error) {
|
func ListUser(nx *nex.Conn, userIds []uint) ([]models.Account, error) {
|
||||||
|
var accounts []models.Account
|
||||||
|
var missingId []uint
|
||||||
|
cachedUsers := make(map[uint]models.Account)
|
||||||
|
|
||||||
|
// Try to get users from cache
|
||||||
|
cacheConn, err := cachekit.NewConn(nx, 3*time.Second)
|
||||||
|
if err == nil {
|
||||||
|
for _, userId := range userIds {
|
||||||
|
key := cachekit.FKey(cachekit.DAAttachment, userId)
|
||||||
|
if user, err := cachekit.Get[models.Account](cacheConn, key); err == nil {
|
||||||
|
cachedUsers[userId] = user
|
||||||
|
} else {
|
||||||
|
missingId = append(missingId, userId)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If all users are found in cache, return them
|
||||||
|
if len(missingId) == 0 {
|
||||||
|
for _, account := range cachedUsers {
|
||||||
|
accounts = append(accounts, account)
|
||||||
|
}
|
||||||
|
return accounts, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fetch missing users from the gRPC service
|
||||||
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
conn, err := nx.GetClientGrpcConn(nex.ServiceTypeAuth)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
raw, _ := proto.NewUserServiceClient(conn).ListUser(context.Background(), &proto.ListUserRequest{
|
raw, _ := proto.NewUserServiceClient(conn).ListUser(context.Background(), &proto.ListUserRequest{
|
||||||
UserId: lo.Map(userId, func(item uint, index int) uint64 {
|
UserId: lo.Map(missingId, func(item uint, index int) uint64 {
|
||||||
return uint64(item)
|
return uint64(item)
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
var out []models.Account
|
|
||||||
|
// Convert fetched users and add to the result
|
||||||
for _, item := range raw.GetData() {
|
for _, item := range raw.GetData() {
|
||||||
out = append(out, GetAccountFromUserInfo(&sec.UserInfo{
|
account := GetAccountFromUserInfo(&sec.UserInfo{
|
||||||
ID: uint(item.GetId()),
|
ID: uint(item.GetId()),
|
||||||
Name: item.GetName(),
|
Name: item.GetName(),
|
||||||
PermNodes: nex.DecodeMap(item.GetPermNodes()),
|
PermNodes: nex.DecodeMap(item.GetPermNodes()),
|
||||||
Metadata: nex.DecodeMap(item.GetMetadata()),
|
Metadata: nex.DecodeMap(item.GetMetadata()),
|
||||||
}))
|
})
|
||||||
|
accounts = append(accounts, account)
|
||||||
}
|
}
|
||||||
return out, nil
|
|
||||||
|
// Merge cached and fetched results
|
||||||
|
for _, account := range cachedUsers {
|
||||||
|
accounts = append(accounts, account)
|
||||||
|
}
|
||||||
|
|
||||||
|
return accounts, nil
|
||||||
}
|
}
|
||||||
|
24
pkg/internal/cache/store.go
vendored
24
pkg/internal/cache/store.go
vendored
@ -1,24 +0,0 @@
|
|||||||
package cache
|
|
||||||
|
|
||||||
import (
|
|
||||||
"github.com/dgraph-io/ristretto"
|
|
||||||
"github.com/eko/gocache/lib/v4/store"
|
|
||||||
ristrettoCache "github.com/eko/gocache/store/ristretto/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
var S store.StoreInterface
|
|
||||||
|
|
||||||
func NewStore() error {
|
|
||||||
ris, err := ristretto.NewCache(&ristretto.Config{
|
|
||||||
NumCounters: 1e7,
|
|
||||||
MaxCost: 1 << 27,
|
|
||||||
BufferItems: 64,
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
S = ristrettoCache.NewRistretto(ris)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -11,6 +11,7 @@ var AutoMaintainRange = []any{
|
|||||||
&models.AccountGroupMember{},
|
&models.AccountGroupMember{},
|
||||||
&models.AuthFactor{},
|
&models.AuthFactor{},
|
||||||
&models.AccountProfile{},
|
&models.AccountProfile{},
|
||||||
|
&models.AccountPage{},
|
||||||
&models.AccountContact{},
|
&models.AccountContact{},
|
||||||
&models.AccountRelationship{},
|
&models.AccountRelationship{},
|
||||||
&models.Status{},
|
&models.Status{},
|
||||||
@ -25,10 +26,13 @@ var AutoMaintainRange = []any{
|
|||||||
&models.NotificationSubscriber{},
|
&models.NotificationSubscriber{},
|
||||||
&models.AuditRecord{},
|
&models.AuditRecord{},
|
||||||
&models.ApiKey{},
|
&models.ApiKey{},
|
||||||
&models.SignRecord{},
|
&models.CheckInRecord{},
|
||||||
&models.PreferenceNotification{},
|
&models.PreferenceNotification{},
|
||||||
&models.PreferenceAuth{},
|
&models.PreferenceAuth{},
|
||||||
&models.AbuseReport{},
|
&models.AbuseReport{},
|
||||||
|
&models.Program{},
|
||||||
|
&models.ProgramMember{},
|
||||||
|
&models.Punishment{},
|
||||||
}
|
}
|
||||||
|
|
||||||
func RunMigration(source *gorm.DB) error {
|
func RunMigration(source *gorm.DB) error {
|
||||||
|
@ -2,8 +2,10 @@ package database
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cruda"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
"github.com/oschwald/geoip2-golang"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -28,3 +30,14 @@ func NewGorm() error {
|
|||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var Gc *geoip2.Reader
|
||||||
|
|
||||||
|
func NewGeoDB() error {
|
||||||
|
conn, err := geoip2.Open(viper.GetString("geoip_db"))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to open geoip database: %v", err)
|
||||||
|
}
|
||||||
|
Gc = conn
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@ -2,18 +2,32 @@ package gap
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/rx"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"git.solsynth.dev/hypernet/pusher/pkg/pushkit/pushcon"
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit/pushcon"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var Nx *nex.Conn
|
var (
|
||||||
var Px *pushcon.Conn
|
Nx *nex.Conn
|
||||||
|
Px *pushcon.Conn
|
||||||
|
Rx *rx.MqConn
|
||||||
|
Ca *cachekit.Conn
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
FactorOtpPrefix = "auth-otp"
|
||||||
|
)
|
||||||
|
|
||||||
func InitializeToNexus() error {
|
func InitializeToNexus() error {
|
||||||
grpcBind := strings.SplitN(viper.GetString("grpc_bind"), ":", 2)
|
grpcBind := strings.SplitN(viper.GetString("grpc_bind"), ":", 2)
|
||||||
@ -46,5 +60,18 @@ func InitializeToNexus() error {
|
|||||||
return fmt.Errorf("error during initialize pushcon: %v", err)
|
return fmt.Errorf("error during initialize pushcon: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rx, err = rx.NewMqConn(Nx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error during initialize nexus rx module: %v", err)
|
||||||
|
}
|
||||||
|
Ca, err = cachekit.NewConn(Nx, time.Second*3)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error during initialize nexus cache module: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func LoadLocalization() error {
|
||||||
|
return localize.LoadLocalization(viper.GetString("locales_dir"), viper.GetString("templates_dir"))
|
||||||
|
}
|
||||||
|
@ -2,6 +2,8 @@ package grpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/proto"
|
"git.solsynth.dev/hypernet/passport/pkg/proto"
|
||||||
)
|
)
|
||||||
@ -10,7 +12,7 @@ func (v *App) RecordEvent(ctx context.Context, request *proto.RecordEventRequest
|
|||||||
services.AddEvent(
|
services.AddEvent(
|
||||||
uint(request.GetUserId()),
|
uint(request.GetUserId()),
|
||||||
request.GetAction(),
|
request.GetAction(),
|
||||||
request.GetTarget(),
|
nex.DecodeMap(request.GetMetadata()),
|
||||||
request.GetIp(),
|
request.GetIp(),
|
||||||
request.GetUserAgent(),
|
request.GetUserAgent(),
|
||||||
)
|
)
|
||||||
|
@ -3,6 +3,7 @@ package grpc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
@ -17,7 +18,7 @@ func (v *App) ListAvailableRealm(ctx context.Context, request *proto.LookupUserR
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("unable to find target account: %v", err)
|
return nil, fmt.Errorf("unable to find target account: %v", err)
|
||||||
}
|
}
|
||||||
realms, err := services.ListAvailableRealm(account)
|
realms, err := services.ListAvailableRealm(account, request.GetIncludePublic())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,11 @@ type App struct {
|
|||||||
nroto.UnimplementedAuthServiceServer
|
nroto.UnimplementedAuthServiceServer
|
||||||
nroto.UnimplementedDirectoryServiceServer
|
nroto.UnimplementedDirectoryServiceServer
|
||||||
nroto.UnimplementedUserServiceServer
|
nroto.UnimplementedUserServiceServer
|
||||||
|
nroto.UnimplementedStreamServiceServer
|
||||||
proto.UnimplementedRealmServiceServer
|
proto.UnimplementedRealmServiceServer
|
||||||
proto.UnimplementedAuditServiceServer
|
proto.UnimplementedAuditServiceServer
|
||||||
proto.UnimplementedNotifyServiceServer
|
proto.UnimplementedNotifyServiceServer
|
||||||
|
proto.UnimplementedThirdClientServiceServer
|
||||||
health.UnimplementedHealthServer
|
health.UnimplementedHealthServer
|
||||||
|
|
||||||
srv *grpc.Server
|
srv *grpc.Server
|
||||||
@ -32,9 +34,12 @@ func NewServer() *App {
|
|||||||
|
|
||||||
nroto.RegisterAuthServiceServer(server.srv, server)
|
nroto.RegisterAuthServiceServer(server.srv, server)
|
||||||
nroto.RegisterUserServiceServer(server.srv, server)
|
nroto.RegisterUserServiceServer(server.srv, server)
|
||||||
|
nroto.RegisterDirectoryServiceServer(server.srv, server)
|
||||||
|
nroto.RegisterStreamServiceServer(server.srv, server)
|
||||||
proto.RegisterNotifyServiceServer(server.srv, server)
|
proto.RegisterNotifyServiceServer(server.srv, server)
|
||||||
proto.RegisterRealmServiceServer(server.srv, server)
|
proto.RegisterRealmServiceServer(server.srv, server)
|
||||||
proto.RegisterAuditServiceServer(server.srv, server)
|
proto.RegisterAuditServiceServer(server.srv, server)
|
||||||
|
proto.RegisterThirdClientServiceServer(server.srv, server)
|
||||||
health.RegisterHealthServer(server.srv, server)
|
health.RegisterHealthServer(server.srv, server)
|
||||||
|
|
||||||
reflection.Register(server.srv)
|
reflection.Register(server.srv)
|
||||||
|
@ -2,13 +2,24 @@ package grpc
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *App) BroadcastEvent(ctx context.Context, request *proto.EventInfo) (*proto.EventResponse, error) {
|
func (v *App) BroadcastEvent(ctx context.Context, request *proto.EventInfo) (*proto.EventResponse, error) {
|
||||||
|
log.Debug().Str("event", request.GetEvent()).
|
||||||
|
Msg("Got a broadcasting event...")
|
||||||
|
|
||||||
switch request.GetEvent() {
|
switch request.GetEvent() {
|
||||||
|
// Last seen at
|
||||||
case "ws.client.register":
|
case "ws.client.register":
|
||||||
// No longer need update user online status
|
// No longer need update user online status
|
||||||
// Based on realtime sever connection status
|
// Based on realtime sever connection status
|
||||||
@ -16,8 +27,99 @@ func (v *App) BroadcastEvent(ctx context.Context, request *proto.EventInfo) (*pr
|
|||||||
case "ws.client.unregister":
|
case "ws.client.unregister":
|
||||||
// Update user last seen at
|
// Update user last seen at
|
||||||
data := nex.DecodeMap(request.GetData())
|
data := nex.DecodeMap(request.GetData())
|
||||||
_ = services.SetAccountLastSeen(uint(data["user"].(float64)))
|
err := services.SetAccountLastSeen(uint(data["user"].(float64)))
|
||||||
|
log.Debug().Err(err).Any("event", data).Msg("Setting account last seen...")
|
||||||
}
|
}
|
||||||
|
|
||||||
return &proto.EventResponse{}, nil
|
return &proto.EventResponse{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v *App) PushStream(_ context.Context, request *proto.PushStreamRequest) (*proto.PushStreamResponse, error) {
|
||||||
|
sc := proto.NewStreamServiceClient(gap.Nx.GetNexusGrpcConn())
|
||||||
|
|
||||||
|
var in nex.WebSocketPackage
|
||||||
|
if err := jsoniter.Unmarshal(request.GetBody(), &in); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch in.Action {
|
||||||
|
// PaKex (Key Exchange)
|
||||||
|
case "kex.ask":
|
||||||
|
var data struct {
|
||||||
|
UserID uint `json:"user_id" validate:"required"`
|
||||||
|
KeypairID string `json:"keypair_id" validate:"required"`
|
||||||
|
ClientID string `json:"client_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := jsoniter.Unmarshal(in.RawPayload(), &data)
|
||||||
|
if request.ClientId != nil {
|
||||||
|
data.ClientID = *request.ClientId
|
||||||
|
}
|
||||||
|
if err == nil {
|
||||||
|
err = exts.ValidateStruct(data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: fmt.Sprintf("unable parse payload: %v", err),
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward ask request
|
||||||
|
sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
UserId: lo.ToPtr(uint64(data.UserID)),
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "kex.ask",
|
||||||
|
Payload: data,
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
case "kex.ack":
|
||||||
|
var data struct {
|
||||||
|
UserID uint `json:"user_id" validate:"required"`
|
||||||
|
KeypairID string `json:"keypair_id" validate:"required"`
|
||||||
|
PublicKey string `json:"public_key"`
|
||||||
|
PrivateKey string `json:"private_key"`
|
||||||
|
ClientID string `json:"client_id" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
err := jsoniter.Unmarshal(in.RawPayload(), &data)
|
||||||
|
if err == nil {
|
||||||
|
err = exts.ValidateStruct(data)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: fmt.Sprintf("unable parse payload: %v", err),
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
if len(data.PublicKey) == 0 && len(data.PrivateKey) == 0 {
|
||||||
|
_, _ = sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: request.ClientId,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "error",
|
||||||
|
Message: "one of public key and private key is required",
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
// Forward ack request
|
||||||
|
sc.PushStream(context.Background(), &proto.PushStreamRequest{
|
||||||
|
ClientId: &data.ClientID,
|
||||||
|
Body: nex.WebSocketPackage{
|
||||||
|
Action: "kex.ack",
|
||||||
|
Payload: data,
|
||||||
|
}.Marshal(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.PushStreamResponse{}, nil
|
||||||
|
}
|
||||||
|
42
pkg/internal/grpc/third_client.go
Normal file
42
pkg/internal/grpc/third_client.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package grpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/proto"
|
||||||
|
"google.golang.org/grpc/codes"
|
||||||
|
"google.golang.org/grpc/status"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (v *App) GetThirdClient(ctx context.Context, request *proto.GetThirdClientRequest) (*proto.GetThirdClientResponse, error) {
|
||||||
|
tx := database.C
|
||||||
|
if request.Id == nil && request.Alias == nil {
|
||||||
|
return nil, status.Error(codes.InvalidArgument, "either id or alias must be specified")
|
||||||
|
}
|
||||||
|
if request.Id != nil {
|
||||||
|
tx = tx.Where("id = ?", request.Id)
|
||||||
|
} else if request.Alias != nil {
|
||||||
|
tx = tx.Where("alias = ?", request.Alias)
|
||||||
|
}
|
||||||
|
|
||||||
|
var client models.ThirdClient
|
||||||
|
if err := tx.First(&client).Error; err != nil {
|
||||||
|
return nil, status.Errorf(codes.NotFound, "requested client was not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if request.Secret != nil {
|
||||||
|
if client.Secret != request.GetSecret() {
|
||||||
|
return nil, status.Errorf(codes.PermissionDenied, "invalid secret")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return &proto.GetThirdClientResponse{
|
||||||
|
Info: &proto.ThirdClientInfo{
|
||||||
|
Id: uint64(client.ID),
|
||||||
|
Name: client.Name,
|
||||||
|
Description: client.Description,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
@ -3,27 +3,29 @@ package grpc
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"google.golang.org/grpc/codes"
|
"google.golang.org/grpc/codes"
|
||||||
"google.golang.org/grpc/status"
|
"google.golang.org/grpc/status"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (v *App) GetUser(ctx context.Context, request *proto.GetUserRequest) (*proto.UserInfo, error) {
|
func (v *App) GetUser(ctx context.Context, request *proto.GetUserRequest) (*proto.UserInfo, error) {
|
||||||
tx := database.C
|
var account models.Account
|
||||||
|
var err error
|
||||||
if request.UserId != nil {
|
if request.UserId != nil {
|
||||||
tx = tx.Where("id = ?", uint(request.GetUserId()))
|
account, err = services.GetAccountForEnd(uint(request.GetUserId()))
|
||||||
}
|
} else if request.Name != nil {
|
||||||
if request.Name != nil {
|
account, err = services.GetAccountForEnd(request.GetName())
|
||||||
tx = tx.Where("name = ?", request.GetName())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var account models.Account
|
if err != nil {
|
||||||
if err := tx.First(&account).Error; err != nil {
|
return nil, status.Errorf(codes.NotFound, fmt.Sprintf("unable to get account punishments: %v", err))
|
||||||
return nil, status.Errorf(codes.NotFound, fmt.Sprintf("requested user with id %d was not found", request.GetUserId()))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return account.EncodeToUserInfo(), nil
|
return account.EncodeToUserInfo(), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -48,7 +50,7 @@ func (v *App) ListUser(ctx context.Context, request *proto.ListUserRequest) (*pr
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v *App) ListUserRelative(ctx context.Context, request *proto.ListUserRelativeRequest) (*proto.ListUserRelativeResponse, error) {
|
func (v *App) ListUserRelative(ctx context.Context, request *proto.ListUserRelativeRequest) (*proto.ListUserRelativeResponse, error) {
|
||||||
tx := database.C.Preload("Account").Where("status = ?", request.GetStatus())
|
tx := database.C.Preload("Account").Preload("Related").Where("status = ?", request.GetStatus())
|
||||||
|
|
||||||
if request.GetIsRelated() {
|
if request.GetIsRelated() {
|
||||||
tx = tx.Where("related_id = ?", request.GetUserId())
|
tx = tx.Where("related_id = ?", request.GetUserId())
|
||||||
@ -63,9 +65,10 @@ func (v *App) ListUserRelative(ctx context.Context, request *proto.ListUserRelat
|
|||||||
|
|
||||||
return &proto.ListUserRelativeResponse{
|
return &proto.ListUserRelativeResponse{
|
||||||
Data: lo.Map(data, func(item models.AccountRelationship, index int) *proto.UserInfo {
|
Data: lo.Map(data, func(item models.AccountRelationship, index int) *proto.UserInfo {
|
||||||
|
account := lo.Ternary(request.GetIsRelated(), item.Account, item.Related)
|
||||||
val := &proto.UserInfo{
|
val := &proto.UserInfo{
|
||||||
Id: uint64(item.AccountID),
|
Id: uint64(account.ID),
|
||||||
Name: item.Account.Name,
|
Name: account.Name,
|
||||||
}
|
}
|
||||||
|
|
||||||
return val
|
return val
|
||||||
|
@ -1,67 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
)
|
|
||||||
|
|
||||||
func notifyUser(c *fiber.Ctx) error {
|
|
||||||
if err := exts.EnsureGrantedPerm(c, "DevNotifyUser", true); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
|
|
||||||
var data struct {
|
|
||||||
ClientID string `json:"client_id" validate:"required"`
|
|
||||||
Topic string `json:"type" validate:"required"`
|
|
||||||
Title string `json:"subject" validate:"required,max=1024"`
|
|
||||||
Subtitle string `json:"subtitle" validate:"max=1024"`
|
|
||||||
Body string `json:"content" validate:"required,max=4096"`
|
|
||||||
Metadata map[string]any `json:"metadata"`
|
|
||||||
Priority int `json:"priority"`
|
|
||||||
IsRealtime bool `json:"is_realtime"`
|
|
||||||
UserID uint `json:"user_id" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
client, err := services.GetThirdClientWithUser(data.ClientID, user.ID)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to get client: %v", err))
|
|
||||||
}
|
|
||||||
|
|
||||||
var target models.Account
|
|
||||||
if target, err = services.GetAccount(data.UserID); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
notification := models.Notification{
|
|
||||||
Topic: data.Topic,
|
|
||||||
Subtitle: data.Subtitle,
|
|
||||||
Title: data.Title,
|
|
||||||
Body: data.Body,
|
|
||||||
Metadata: data.Metadata,
|
|
||||||
Priority: data.Priority,
|
|
||||||
Account: target,
|
|
||||||
AccountID: target.ID,
|
|
||||||
SenderID: &client.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if data.IsRealtime {
|
|
||||||
if err := services.PushNotification(notification); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if err := services.NewNotification(notification); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
@ -3,14 +3,18 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"maps"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"time"
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
|
||||||
|
"gorm.io/gorm"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
@ -18,10 +22,30 @@ import (
|
|||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/google/uuid"
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func KgAccountCache(query any) string {
|
||||||
|
return cachekit.FKey(cachekit.DAUser, query)
|
||||||
|
}
|
||||||
|
|
||||||
|
func CacheAccount(account models.Account) {
|
||||||
|
cachekit.Set[models.Account](
|
||||||
|
gap.Ca,
|
||||||
|
KgAccountCache(account.Name),
|
||||||
|
account,
|
||||||
|
60*time.Minute,
|
||||||
|
fmt.Sprintf("user#%d", account.ID),
|
||||||
|
)
|
||||||
|
cachekit.Set[models.Account](
|
||||||
|
gap.Ca,
|
||||||
|
KgAccountCache(account.ID),
|
||||||
|
account,
|
||||||
|
60*time.Minute,
|
||||||
|
fmt.Sprintf("user#%d", account.ID),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
func ValidateAccountName(val string, min, max int) bool {
|
func ValidateAccountName(val string, min, max int) bool {
|
||||||
actualLength := 0
|
actualLength := 0
|
||||||
for _, r := range val {
|
for _, r := range val {
|
||||||
@ -34,6 +58,58 @@ func ValidateAccountName(val string, min, max int) bool {
|
|||||||
return actualLength >= min && max >= actualLength
|
return actualLength >= min && max >= actualLength
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetAccountForEnd(id any) (models.Account, error) {
|
||||||
|
if val, err := cachekit.Get[models.Account](gap.Ca, KgAccountCache(id)); err == nil {
|
||||||
|
return val, err
|
||||||
|
}
|
||||||
|
|
||||||
|
var account models.Account
|
||||||
|
tx := database.C
|
||||||
|
switch id.(type) {
|
||||||
|
case uint:
|
||||||
|
tx = tx.Where("id = ?", id)
|
||||||
|
case string:
|
||||||
|
tx = tx.Where("name = ?", id)
|
||||||
|
default:
|
||||||
|
return account, fmt.Errorf("invalid account id type")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.
|
||||||
|
Preload("Profile").
|
||||||
|
Preload("Badges", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Order("badges.is_active DESC, badges.type DESC")
|
||||||
|
}).
|
||||||
|
First(&account).Error; err != nil {
|
||||||
|
return account, fmt.Errorf("requested user with id %d was not found", id)
|
||||||
|
}
|
||||||
|
|
||||||
|
groups, err := GetUserAccountGroup(account)
|
||||||
|
if err != nil {
|
||||||
|
return account, fmt.Errorf("unable to get account groups: %v", err)
|
||||||
|
}
|
||||||
|
for _, group := range groups {
|
||||||
|
for k, v := range group.PermNodes {
|
||||||
|
if _, ok := account.PermNodes[k]; !ok {
|
||||||
|
account.PermNodes[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
punishments, err := ListPunishments(account)
|
||||||
|
if err != nil {
|
||||||
|
return account, fmt.Errorf("unable to get account punishments: %v", err)
|
||||||
|
}
|
||||||
|
account.Punishments = punishments
|
||||||
|
for _, punishment := range punishments {
|
||||||
|
if punishment.Type == models.PunishmentTypeLimited && len(punishment.PermNodes) > 0 {
|
||||||
|
maps.Copy(account.PermNodes, punishment.PermNodes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CacheAccount(account)
|
||||||
|
|
||||||
|
return account, nil
|
||||||
|
}
|
||||||
|
|
||||||
func GetAccount(id uint) (models.Account, error) {
|
func GetAccount(id uint) (models.Account, error) {
|
||||||
var account models.Account
|
var account models.Account
|
||||||
if err := database.C.Where(models.Account{
|
if err := database.C.Where(models.Account{
|
||||||
@ -93,7 +169,7 @@ func SearchAccount(probe string) ([]models.Account, error) {
|
|||||||
return accounts, nil
|
return accounts, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAccount(name, nick, email, password string) (models.Account, error) {
|
func CreateAccount(name, nick, email, password, lang string) (models.Account, error) {
|
||||||
user := models.Account{
|
user := models.Account{
|
||||||
Name: name,
|
Name: name,
|
||||||
Nick: nick,
|
Nick: nick,
|
||||||
@ -105,10 +181,6 @@ func CreateAccount(name, nick, email, password string) (models.Account, error) {
|
|||||||
Type: models.PasswordAuthFactor,
|
Type: models.PasswordAuthFactor,
|
||||||
Secret: HashPassword(password),
|
Secret: HashPassword(password),
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Type: models.EmailPasswordFactor,
|
|
||||||
Secret: uuid.NewString()[:8],
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
Contacts: []models.AccountContact{
|
Contacts: []models.AccountContact{
|
||||||
{
|
{
|
||||||
@ -118,18 +190,15 @@ func CreateAccount(name, nick, email, password string) (models.Account, error) {
|
|||||||
VerifiedAt: nil,
|
VerifiedAt: nil,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
Language: lang,
|
||||||
PermNodes: datatypes.JSONMap{},
|
PermNodes: datatypes.JSONMap{},
|
||||||
ConfirmedAt: nil,
|
ConfirmedAt: nil,
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := database.C.Create(&user).Error; err != nil {
|
if err := database.C.Create(&user).Error; err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
} else if viper.GetInt("default_user_group") > 0 {
|
|
||||||
database.C.Create(&models.AccountGroupMember{
|
|
||||||
AccountID: user.ID,
|
|
||||||
GroupID: uint(viper.GetInt("default_user_group")),
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
// Only gave user permission group after they confiremd the registeration
|
||||||
|
|
||||||
if tk, err := NewMagicToken(models.ConfirmMagicToken, &user, nil); err != nil {
|
if tk, err := NewMagicToken(models.ConfirmMagicToken, &user, nil); err != nil {
|
||||||
return user, err
|
return user, err
|
||||||
@ -167,19 +236,22 @@ func ConfirmAccount(code string) error {
|
|||||||
func ForceConfirmAccount(user models.Account) error {
|
func ForceConfirmAccount(user models.Account) error {
|
||||||
user.ConfirmedAt = lo.ToPtr(time.Now())
|
user.ConfirmedAt = lo.ToPtr(time.Now())
|
||||||
|
|
||||||
for k, v := range viper.GetStringMap("permissions.verified") {
|
if viper.GetInt("default_user_group") > 0 {
|
||||||
if val, ok := user.PermNodes[k]; !ok {
|
database.C.Create(&models.AccountGroupMember{
|
||||||
user.PermNodes[k] = v
|
AccountID: user.ID,
|
||||||
} else {
|
GroupID: uint(viper.GetInt("default_user_group")),
|
||||||
user.PermNodes[k] = val
|
})
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = database.C.Model(&models.AccountContact{}).Where("account_id = ?", user.ID).Updates(&models.AccountContact{
|
||||||
|
VerifiedAt: lo.ToPtr(time.Now()),
|
||||||
|
})
|
||||||
|
|
||||||
if err := database.C.Save(&user).Error; err != nil {
|
if err := database.C.Save(&user).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
InvalidAuthCacheWithUser(user.ID)
|
InvalidUserAuthCache(user.ID)
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@ -304,6 +376,10 @@ func ConfirmResetPassword(code, newPassword string) error {
|
|||||||
func DeleteAccount(id uint) error {
|
func DeleteAccount(id uint) error {
|
||||||
tx := database.C.Begin()
|
tx := database.C.Begin()
|
||||||
|
|
||||||
|
if err := tx.Delete(&models.AuthTicket{}, "account_id = ?", id).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
if err := tx.Select(clause.Associations).Delete(&models.Account{}, "id = ?", id).Error; err != nil {
|
if err := tx.Select(clause.Associations).Delete(&models.Account{}, "id = ?", id).Error; err != nil {
|
||||||
tx.Rollback()
|
tx.Rollback()
|
||||||
return err
|
return err
|
||||||
@ -312,12 +388,13 @@ func DeleteAccount(id uint) error {
|
|||||||
if err := tx.Commit().Error; err != nil {
|
if err := tx.Commit().Error; err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
InvalidAuthCacheWithUser(id)
|
InvalidUserAuthCache(id)
|
||||||
_, _ = proto.NewDirectoryServiceClient(gap.Nx.GetNexusGrpcConn()).BroadcastEvent(context.Background(), &proto.EventInfo{
|
conn := gap.Nx.GetNexusGrpcConn()
|
||||||
|
_, _ = proto.NewDirectoryServiceClient(conn).BroadcastEvent(context.Background(), &proto.EventInfo{
|
||||||
Event: "deletion",
|
Event: "deletion",
|
||||||
Data: nex.EncodeMap(map[string]any{
|
Data: nex.EncodeMap(map[string]any{
|
||||||
"type": "account",
|
"type": "account",
|
||||||
"id": fmt.Sprintf("%d", id),
|
"id": id,
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,18 +1,16 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/eko/gocache/lib/v4/cache"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
"github.com/eko/gocache/lib/v4/marshaler"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"github.com/eko/gocache/lib/v4/store"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
|
||||||
localCache "git.solsynth.dev/hypernet/passport/pkg/internal/cache"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@ -32,24 +30,26 @@ func Authenticate(sessionId uint) (ctx models.AuthTicket, perms map[string]any,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAuthContextCacheKey(sessionId uint) string {
|
func KgAuthContextCache(sessionId uint) string {
|
||||||
return fmt.Sprintf("auth-context#%d", sessionId)
|
return cachekit.FKey("auth-context", sessionId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAuthContext(sessionId uint) (models.AuthTicket, error) {
|
func GetAuthContext(sessionId uint) (models.AuthTicket, error) {
|
||||||
var err error
|
var err error
|
||||||
var ctx models.AuthTicket
|
var ctx models.AuthTicket
|
||||||
|
|
||||||
cacheManager := cache.New[any](localCache.S)
|
key := KgAuthContextCache(sessionId)
|
||||||
marshal := marshaler.New(cacheManager)
|
if val, err := cachekit.Get[models.AuthTicket](gap.Ca, key); err == nil {
|
||||||
|
ctx = val
|
||||||
key := GetAuthContextCacheKey(sessionId)
|
|
||||||
if val, err := marshal.Get(context.Background(), key, new(models.AuthTicket)); err == nil {
|
|
||||||
ctx = *val.(*models.AuthTicket)
|
|
||||||
} else {
|
} else {
|
||||||
|
log.Error().Err(err).Msg("Unable to get auth context cache")
|
||||||
ctx, err = CacheAuthContext(sessionId)
|
ctx, err = CacheAuthContext(sessionId)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Unable to cache auth context")
|
||||||
|
} else {
|
||||||
log.Debug().Uint("session", sessionId).Msg("Created a new auth context cache")
|
log.Debug().Uint("session", sessionId).Msg("Created a new auth context cache")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return ctx, err
|
return ctx, err
|
||||||
}
|
}
|
||||||
@ -59,7 +59,6 @@ func CacheAuthContext(sessionId uint) (models.AuthTicket, error) {
|
|||||||
var ticket models.AuthTicket
|
var ticket models.AuthTicket
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where("id = ?", sessionId).
|
Where("id = ?", sessionId).
|
||||||
Preload("Account").
|
|
||||||
First(&ticket).Error; err != nil {
|
First(&ticket).Error; err != nil {
|
||||||
return ticket, fmt.Errorf("invalid auth ticket: %v", err)
|
return ticket, fmt.Errorf("invalid auth ticket: %v", err)
|
||||||
} else if err := ticket.IsAvailable(); err != nil {
|
} else if err := ticket.IsAvailable(); err != nil {
|
||||||
@ -85,27 +84,22 @@ func CacheAuthContext(sessionId uint) (models.AuthTicket, error) {
|
|||||||
ticket.Account = user
|
ticket.Account = user
|
||||||
|
|
||||||
// Put the data into the cache
|
// Put the data into the cache
|
||||||
cacheManager := cache.New[any](localCache.S)
|
key := KgAuthContextCache(sessionId)
|
||||||
marshal := marshaler.New(cacheManager)
|
err = cachekit.Set[models.AuthTicket](
|
||||||
|
gap.Ca,
|
||||||
key := GetAuthContextCacheKey(sessionId)
|
|
||||||
err = marshal.Set(
|
|
||||||
context.Background(),
|
|
||||||
key,
|
key,
|
||||||
ticket,
|
ticket,
|
||||||
store.WithExpiration(10*time.Minute),
|
time.Minute*10,
|
||||||
store.WithTags([]string{"auth-context", fmt.Sprintf("user#%d", user.ID)}),
|
"auth-context",
|
||||||
|
fmt.Sprintf("user#%d", user.ID),
|
||||||
)
|
)
|
||||||
|
if err != nil {
|
||||||
return ticket, nil
|
log.Error().Err(err).Msg("Unable to cache auth context...")
|
||||||
}
|
}
|
||||||
|
|
||||||
func InvalidAuthCacheWithUser(userId uint) {
|
return ticket, err
|
||||||
cacheManager := cache.New[any](localCache.S)
|
}
|
||||||
ctx := context.Background()
|
|
||||||
|
func InvalidUserAuthCache(uid uint) {
|
||||||
cacheManager.Invalidate(
|
cachekit.DeleteByTags(gap.Ca, "auth-context", fmt.Sprintf("user#%d", uid))
|
||||||
ctx,
|
|
||||||
store.WithInvalidateTags([]string{"auth-context", fmt.Sprintf("user#%d", userId)}),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -13,3 +13,23 @@ func GrantBadge(user models.Account, badge models.Badge) error {
|
|||||||
func RevokeBadge(badge models.Badge) error {
|
func RevokeBadge(badge models.Badge) error {
|
||||||
return database.C.Delete(&badge).Error
|
return database.C.Delete(&badge).Error
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ActiveBadge(badge models.Badge) error {
|
||||||
|
accountId := badge.AccountID
|
||||||
|
tx := database.C.Begin()
|
||||||
|
|
||||||
|
if err := tx.Model(&models.Badge{}).Where("account_id = ?", accountId).Update("is_active", false).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Model(&models.Badge{}).Where("id = ?", badge.ID).Update("is_active", true).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
139
pkg/internal/services/check_in.go
Normal file
139
pkg/internal/services/check_in.go
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"math/rand"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/wallet/pkg/proto"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
func CheckCanCheckIn(user models.Account) error {
|
||||||
|
var record models.CheckInRecord
|
||||||
|
if err := database.C.Where("account_id = ? AND created_at::date = CURRENT_DATE", user.ID).First(&record).Error; err != nil {
|
||||||
|
if errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("unable get check in record: %v", err)
|
||||||
|
}
|
||||||
|
return fmt.Errorf("today's check in record exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetCheckInStreak(user models.Account) (int64, error) {
|
||||||
|
var streaks int64
|
||||||
|
if err := database.C.Raw(`WITH dates AS (
|
||||||
|
SELECT DISTINCT created_at::DATE AS created_date
|
||||||
|
FROM check_in_records
|
||||||
|
WHERE created_at::DATE <= CURRENT_DATE
|
||||||
|
AND account_id = ?
|
||||||
|
),
|
||||||
|
streak AS (
|
||||||
|
SELECT created_date,
|
||||||
|
created_date - INTERVAL '1 day' * (ROW_NUMBER() OVER (ORDER BY created_date)) AS grp
|
||||||
|
FROM dates
|
||||||
|
),
|
||||||
|
grouped_streaks AS (
|
||||||
|
SELECT grp, COUNT(*) AS streak_length, MAX(created_date) AS last_date
|
||||||
|
FROM streak
|
||||||
|
GROUP BY grp
|
||||||
|
),
|
||||||
|
last_streak AS (
|
||||||
|
SELECT streak_length
|
||||||
|
FROM grouped_streaks
|
||||||
|
WHERE last_date = (SELECT MAX(created_date) FROM dates)
|
||||||
|
)
|
||||||
|
SELECT COALESCE(streak_length, 0) FROM last_streak;`, user.ID).Scan(&streaks).Error; err != nil {
|
||||||
|
return streaks, err
|
||||||
|
}
|
||||||
|
return streaks, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetTodayCheckIn(user models.Account) (models.CheckInRecord, error) {
|
||||||
|
var record models.CheckInRecord
|
||||||
|
if err := database.C.Where("account_id = ? AND created_at::date = CURRENT_DATE", user.ID).First(&record).Error; err != nil {
|
||||||
|
return record, fmt.Errorf("unable get check in record: %v", err)
|
||||||
|
}
|
||||||
|
return record, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
const CheckInResultModifiersLength = 4
|
||||||
|
|
||||||
|
func CheckIn(user models.Account) (models.CheckInRecord, error) {
|
||||||
|
var record models.CheckInRecord
|
||||||
|
if err := CheckCanCheckIn(user); err != nil {
|
||||||
|
return record, fmt.Errorf("today already signed")
|
||||||
|
}
|
||||||
|
|
||||||
|
tier := rand.Intn(5)
|
||||||
|
streak, _ := GetCheckInStreak(user)
|
||||||
|
|
||||||
|
expMin := 100
|
||||||
|
exp := expMin + int(math.Max(float64(streak)*5, 10*5))
|
||||||
|
|
||||||
|
coinMax := 10.0 * float64(tier+1)
|
||||||
|
coinMin := 10.0
|
||||||
|
rawCoins := coinMax + rand.Float64()*(coinMax-coinMin) + math.Max(float64(streak)*0.5, float64(100*0.5))
|
||||||
|
|
||||||
|
record = models.CheckInRecord{
|
||||||
|
ResultTier: tier,
|
||||||
|
ResultExperience: exp,
|
||||||
|
ResultCoin: float64(int(rawCoins*100)) / 100,
|
||||||
|
CurrentStreak: int(streak),
|
||||||
|
AccountID: user.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
modifiers := make([]int, CheckInResultModifiersLength)
|
||||||
|
for i := 0; i < CheckInResultModifiersLength; i++ {
|
||||||
|
modifiers[i] = rand.Intn(1025) // from 0 to 1024 as the comment said
|
||||||
|
}
|
||||||
|
record.ResultModifiers = modifiers
|
||||||
|
|
||||||
|
tx := database.C.Begin()
|
||||||
|
|
||||||
|
var profile models.AccountProfile
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).First(&profile).Error; err != nil {
|
||||||
|
return record, fmt.Errorf("unable get account profile: %v", err)
|
||||||
|
} else {
|
||||||
|
profile.Experience += uint64(record.ResultExperience)
|
||||||
|
if err := tx.Save(&profile).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return record, fmt.Errorf("unable update account profile: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := gap.Nx.GetClientGrpcConn("wa")
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Unable to connect with wallet to send daily rewards")
|
||||||
|
record.ResultCoin = 0
|
||||||
|
}
|
||||||
|
wc := proto.NewPaymentServiceClient(conn)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
_, err = wc.MakeTransactionWithAccount(ctx, &proto.MakeTransactionWithAccountRequest{
|
||||||
|
PayeeAccountId: lo.ToPtr(uint64(user.ID)),
|
||||||
|
Amount: record.ResultCoin,
|
||||||
|
Currency: "normal",
|
||||||
|
Remark: "Daily Check-In Rewards",
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Msg("Unable to make transaction with account to send daily rewards")
|
||||||
|
record.ResultCoin = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Save(&record).Error; err != nil {
|
||||||
|
return record, fmt.Errorf("unable do check in: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit()
|
||||||
|
|
||||||
|
return record, nil
|
||||||
|
}
|
@ -1,27 +1,22 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func DoAutoDatabaseCleanup() {
|
func DoAutoDatabaseCleanup() {
|
||||||
log.Debug().Msg("Now cleaning up entire database...")
|
log.Debug().Msg("Now cleaning up entire database...")
|
||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
for _, model := range database.AutoMaintainRange {
|
|
||||||
tx := database.C.Unscoped().Delete(model, "deleted_at IS NOT NULL")
|
|
||||||
if tx.Error != nil {
|
|
||||||
log.Error().Err(tx.Error).Msg("An error occurred when running cleaning up entire database...")
|
|
||||||
}
|
|
||||||
count += tx.RowsAffected
|
|
||||||
}
|
|
||||||
|
|
||||||
deadline := time.Now().Add(-30 * 24 * time.Hour)
|
deadline := time.Now().Add(-30 * 24 * time.Hour)
|
||||||
seenDeadline := time.Now().Add(-7 * 24 * time.Hour)
|
seenDeadline := time.Now().Add(-7 * 24 * time.Hour)
|
||||||
database.C.Unscoped().Where("created_at <= ? OR read_at <= ?", deadline, seenDeadline).Delete(&models.Notification{})
|
tx := database.C.Unscoped().Where("created_at <= ? OR read_at <= ?", deadline, seenDeadline).Delete(&models.Notification{})
|
||||||
|
count += tx.RowsAffected
|
||||||
|
|
||||||
log.Debug().Int64("affected", count).Msg("Clean up entire database accomplished.")
|
log.Debug().Int64("affected", count).Msg("Clean up entire database accomplished.")
|
||||||
}
|
}
|
||||||
|
@ -1,32 +1,72 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
var writeEventQueue []models.ActionEvent
|
var (
|
||||||
var writeAuditQueue []models.AuditRecord
|
writeEventQueue []models.ActionEvent
|
||||||
|
writeAuditQueue []models.AuditRecord
|
||||||
|
)
|
||||||
|
|
||||||
// AddEvent to keep operation logs by user themselves clear to query
|
// AddEvent to keep operation logs by user themselves clear to query
|
||||||
func AddEvent(user uint, event, target, ip, ua string) {
|
func AddEvent(user uint, event string, meta map[string]any, ip, ua string) {
|
||||||
|
var location *string
|
||||||
|
var coordinateX, coordinateY *float64
|
||||||
|
netIp := net.ParseIP(ip)
|
||||||
|
record, err := database.Gc.City(netIp)
|
||||||
|
if err == nil {
|
||||||
|
var locationNames []string
|
||||||
|
locationNames = append(locationNames, record.City.Names["en"])
|
||||||
|
for _, subs := range record.Subdivisions {
|
||||||
|
locationNames = append(locationNames, subs.Names["en"])
|
||||||
|
}
|
||||||
|
location = lo.ToPtr(strings.Join(locationNames, ", "))
|
||||||
|
coordinateX = &record.Location.Latitude
|
||||||
|
coordinateY = &record.Location.Longitude
|
||||||
|
}
|
||||||
writeEventQueue = append(writeEventQueue, models.ActionEvent{
|
writeEventQueue = append(writeEventQueue, models.ActionEvent{
|
||||||
Type: event,
|
Type: event,
|
||||||
Target: target,
|
Metadata: meta,
|
||||||
IpAddress: ip,
|
IpAddress: ip,
|
||||||
UserAgent: ua,
|
UserAgent: ua,
|
||||||
|
Location: location,
|
||||||
|
CoordinateX: coordinateX,
|
||||||
|
CoordinateY: coordinateY,
|
||||||
AccountID: user,
|
AccountID: user,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// AddAuditRecord to keep logs to make administrators' operations clear to query
|
// AddAuditRecord to keep logs to make administrators' operations clear to query
|
||||||
func AddAuditRecord(operator models.Account, act, ip, ua string, metadata map[string]any) {
|
func AddAuditRecord(operator models.Account, act, ip, ua string, metadata map[string]any) {
|
||||||
|
var location *string
|
||||||
|
var coordinateX, coordinateY *float64
|
||||||
|
netIp := net.ParseIP(ip)
|
||||||
|
record, err := database.Gc.City(netIp)
|
||||||
|
if err == nil {
|
||||||
|
var locationNames []string
|
||||||
|
locationNames = append(locationNames, record.City.Names["en"])
|
||||||
|
for _, subs := range record.Subdivisions {
|
||||||
|
locationNames = append(locationNames, subs.Names["en"])
|
||||||
|
}
|
||||||
|
location = lo.ToPtr(strings.Join(locationNames, ", "))
|
||||||
|
coordinateX = &record.Location.Latitude
|
||||||
|
coordinateY = &record.Location.Longitude
|
||||||
|
}
|
||||||
writeAuditQueue = append(writeAuditQueue, models.AuditRecord{
|
writeAuditQueue = append(writeAuditQueue, models.AuditRecord{
|
||||||
Action: act,
|
Action: act,
|
||||||
Metadata: metadata,
|
Metadata: metadata,
|
||||||
IpAddress: ip,
|
IpAddress: ip,
|
||||||
UserAgent: ua,
|
UserAgent: ua,
|
||||||
|
Location: location,
|
||||||
|
CoordinateX: coordinateX,
|
||||||
|
CoordinateY: coordinateY,
|
||||||
AccountID: operator.ID,
|
AccountID: operator.ID,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,32 +2,23 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"strings"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const EmailPasswordTemplate = `Dear %s,
|
|
||||||
|
|
||||||
We hope this message finds you well.
|
|
||||||
As part of our ongoing commitment to ensuring the security of your account, we require you to complete the login process by entering the verification code below:
|
|
||||||
|
|
||||||
Your Login Verification Code: %s
|
|
||||||
|
|
||||||
Please use the provided code within the next 2 hours to complete your login.
|
|
||||||
If you did not request this code, please update your information, maybe your username or email has been leak.
|
|
||||||
|
|
||||||
Thank you for your cooperation in helping us maintain the security of your account.
|
|
||||||
|
|
||||||
Best regards,
|
|
||||||
%s`
|
|
||||||
|
|
||||||
func GetPasswordTypeFactor(userId uint) (models.AuthFactor, error) {
|
func GetPasswordTypeFactor(userId uint) (models.AuthFactor, error) {
|
||||||
var factor models.AuthFactor
|
var factor models.AuthFactor
|
||||||
err := database.C.Where(models.AuthFactor{
|
err := database.C.Where(models.AuthFactor{
|
||||||
@ -65,8 +56,39 @@ func CountUserFactor(userId uint) int64 {
|
|||||||
return count
|
return count
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetFactorCode(factor models.AuthFactor) (bool, error) {
|
func GetFactorCode(factor models.AuthFactor, ip string) (bool, error) {
|
||||||
switch factor.Type {
|
switch factor.Type {
|
||||||
|
case models.InAppNotifyFactor:
|
||||||
|
var user models.Account
|
||||||
|
if err := database.C.Where(&models.Account{
|
||||||
|
BaseModel: models.BaseModel{ID: factor.AccountID},
|
||||||
|
}).First(&user).Error; err != nil {
|
||||||
|
return true, err
|
||||||
|
}
|
||||||
|
|
||||||
|
secret := uuid.NewString()[:6]
|
||||||
|
|
||||||
|
identifier := fmt.Sprintf("%s#%d", gap.FactorOtpPrefix, factor.ID)
|
||||||
|
err := cachekit.Set(gap.Ca, identifier, secret, time.Minute*30, fmt.Sprintf("user#%d", factor.AccountID))
|
||||||
|
if err != nil {
|
||||||
|
return true, fmt.Errorf("error during creating otp: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Info().Uint("factor", factor.ID).Str("secret", secret).Msg("Created one-time-password in cache...")
|
||||||
|
}
|
||||||
|
|
||||||
|
err = NewNotification(models.Notification{
|
||||||
|
Topic: "passport.security.otp",
|
||||||
|
Title: localize.L.GetLocalizedString("subjectLoginOneTimePassword", user.Language),
|
||||||
|
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyLoginOneTimePassword", user.Language), secret),
|
||||||
|
Account: user,
|
||||||
|
AccountID: user.ID,
|
||||||
|
Metadata: map[string]any{"secret": secret},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Uint("factor", factor.ID).Msg("Failed to delivery one-time-password via notify...")
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
case models.EmailPasswordFactor:
|
case models.EmailPasswordFactor:
|
||||||
var user models.Account
|
var user models.Account
|
||||||
if err := database.C.Where(&models.Account{
|
if err := database.C.Where(&models.Account{
|
||||||
@ -75,19 +97,30 @@ func GetFactorCode(factor models.AuthFactor) (bool, error) {
|
|||||||
return true, err
|
return true, err
|
||||||
}
|
}
|
||||||
|
|
||||||
factor.Secret = uuid.NewString()[:6]
|
secret := uuid.NewString()[:6]
|
||||||
if err := database.C.Save(&factor).Error; err != nil {
|
|
||||||
return true, err
|
identifier := fmt.Sprintf("%s#%d", gap.FactorOtpPrefix, factor.ID)
|
||||||
|
err := cachekit.Set(gap.Ca, identifier, secret, time.Minute*30, fmt.Sprintf("user#%d", factor.AccountID))
|
||||||
|
if err != nil {
|
||||||
|
return true, fmt.Errorf("error during creating otp: %v", err)
|
||||||
|
} else {
|
||||||
|
log.Info().Uint("factor", factor.ID).Str("secret", secret).Msg("Created one-time-password in cache...")
|
||||||
}
|
}
|
||||||
|
|
||||||
subject := fmt.Sprintf("[%s] Login verification code", viper.GetString("name"))
|
subject := fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectLoginOneTimePassword", user.Language))
|
||||||
content := fmt.Sprintf(EmailPasswordTemplate, user.Name, factor.Secret, viper.GetString("maintainer"))
|
|
||||||
|
|
||||||
err := gap.Px.PushEmail(pushkit.EmailDeliverRequest{
|
content := localize.L.RenderLocalizedTemplateHTML("email-otp.tmpl", user.Language, map[string]any{
|
||||||
|
"Code": secret,
|
||||||
|
"User": user,
|
||||||
|
"IP": ip,
|
||||||
|
"Date": time.Now().Format(time.DateTime),
|
||||||
|
})
|
||||||
|
|
||||||
|
err = gap.Px.PushEmail(pushkit.EmailDeliverRequest{
|
||||||
To: user.GetPrimaryEmail().Content,
|
To: user.GetPrimaryEmail().Content,
|
||||||
Email: pushkit.EmailData{
|
Email: pushkit.EmailData{
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Text: &content,
|
HTML: &content,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -95,7 +128,6 @@ func GetFactorCode(factor models.AuthFactor) (bool, error) {
|
|||||||
return true, nil
|
return true, nil
|
||||||
}
|
}
|
||||||
return true, nil
|
return true, nil
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
@ -109,12 +141,29 @@ func CheckFactor(factor models.AuthFactor, code string) error {
|
|||||||
nil,
|
nil,
|
||||||
fmt.Errorf("invalid password"),
|
fmt.Errorf("invalid password"),
|
||||||
)
|
)
|
||||||
case models.EmailPasswordFactor:
|
case models.TimeOtpFactor:
|
||||||
return lo.Ternary(
|
return lo.Ternary(
|
||||||
strings.ToUpper(code) == strings.ToUpper(factor.Secret),
|
totp.Validate(code, factor.Secret),
|
||||||
nil,
|
nil,
|
||||||
fmt.Errorf("invalid verification code"),
|
fmt.Errorf("invalid verification code"),
|
||||||
)
|
)
|
||||||
|
case models.InAppNotifyFactor:
|
||||||
|
case models.EmailPasswordFactor:
|
||||||
|
identifier := fmt.Sprintf("%s#%d", gap.FactorOtpPrefix, factor.ID)
|
||||||
|
val, err := cachekit.Get[string](gap.Ca, identifier)
|
||||||
|
if err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error fetching message when validating factor code...")
|
||||||
|
return fmt.Errorf("one-time-password not found or expired")
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.EqualFold(code, val) {
|
||||||
|
return fmt.Errorf("invalid verification code")
|
||||||
|
}
|
||||||
|
log.Info().Uint("factor", factor.ID).Str("secret", code).Msg("Verified one-time-password...")
|
||||||
|
if err := cachekit.Delete(gap.Ca, identifier); err != nil {
|
||||||
|
log.Error().Err(err).Msg("Error deleting the otp from cache...")
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -3,12 +3,13 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||||
"reflect"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
@ -24,7 +25,10 @@ func AddNotifySubscriber(user models.Account, provider, id, tk, ua string) (mode
|
|||||||
if err := database.C.Where(&models.NotificationSubscriber{
|
if err := database.C.Where(&models.NotificationSubscriber{
|
||||||
DeviceID: id,
|
DeviceID: id,
|
||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}); err != nil {
|
}).Or(&models.NotificationSubscriber{
|
||||||
|
DeviceToken: tk,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}).First(&prev).Error; err != nil {
|
||||||
subscriber = models.NotificationSubscriber{
|
subscriber = models.NotificationSubscriber{
|
||||||
UserAgent: ua,
|
UserAgent: ua,
|
||||||
Provider: provider,
|
Provider: provider,
|
||||||
@ -33,12 +37,11 @@ func AddNotifySubscriber(user models.Account, provider, id, tk, ua string) (mode
|
|||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
prev = subscriber
|
subscriber = prev
|
||||||
}
|
|
||||||
|
|
||||||
subscriber.UserAgent = ua
|
subscriber.UserAgent = ua
|
||||||
subscriber.Provider = provider
|
subscriber.Provider = provider
|
||||||
subscriber.DeviceToken = tk
|
subscriber.DeviceToken = tk
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
if !reflect.DeepEqual(subscriber, prev) {
|
if !reflect.DeepEqual(subscriber, prev) {
|
||||||
@ -131,9 +134,10 @@ func PushNotification(notification models.Notification, skipNotifiableCheck ...b
|
|||||||
|
|
||||||
log.Debug().Str("topic", notification.Topic).Any("uid", notification.AccountID).Msg("Pushing notify to user...")
|
log.Debug().Str("topic", notification.Topic).Any("uid", notification.AccountID).Msg("Pushing notify to user...")
|
||||||
|
|
||||||
ctx, cancel = context.WithTimeout(context.Background(), 5*time.Second)
|
|
||||||
defer cancel()
|
|
||||||
err = gap.Px.PushNotifyBatch(pushkit.NotificationPushBatchRequest{
|
err = gap.Px.PushNotifyBatch(pushkit.NotificationPushBatchRequest{
|
||||||
|
Lang: lo.Map(subscribers, func(item models.NotificationSubscriber, index int) string {
|
||||||
|
return notification.Account.Language
|
||||||
|
}),
|
||||||
Providers: providers,
|
Providers: providers,
|
||||||
Tokens: tokens,
|
Tokens: tokens,
|
||||||
Notification: notification.EncodeToPushkit(),
|
Notification: notification.EncodeToPushkit(),
|
||||||
@ -214,6 +218,14 @@ func PushNotificationBatch(notifications []models.Notification, skipNotifiableCh
|
|||||||
}
|
}
|
||||||
|
|
||||||
if err := gap.Px.PushNotifyBatch(pushkit.NotificationPushBatchRequest{
|
if err := gap.Px.PushNotifyBatch(pushkit.NotificationPushBatchRequest{
|
||||||
|
Lang: lo.Map(subscribers, func(item models.NotificationSubscriber, index int) string {
|
||||||
|
for idx := 0; idx < len(notifications); idx++ {
|
||||||
|
if item.AccountID == notifications[idx].AccountID {
|
||||||
|
return notifications[idx].Account.Language
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "en-US"
|
||||||
|
}),
|
||||||
Providers: providers,
|
Providers: providers,
|
||||||
Tokens: tokens,
|
Tokens: tokens,
|
||||||
Notification: notifications[0].EncodeToPushkit(),
|
Notification: notifications[0].EncodeToPushkit(),
|
||||||
|
@ -1,17 +1,15 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"github.com/rs/zerolog/log"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
localCache "git.solsynth.dev/hypernet/passport/pkg/internal/cache"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/eko/gocache/lib/v4/cache"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"github.com/eko/gocache/lib/v4/marshaler"
|
|
||||||
"github.com/eko/gocache/lib/v4/store"
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
||||||
)
|
)
|
||||||
@ -41,46 +39,39 @@ func UpdateAuthPreference(account models.Account, config models.AuthConfig) (mod
|
|||||||
return auth, err
|
return auth, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNotificationPreferenceCacheKey(accountId uint) string {
|
func KgNotifyPreferenceCache(accountId uint) string {
|
||||||
return fmt.Sprintf("notification-preference#%d", accountId)
|
return fmt.Sprintf("notification-preference#%d", accountId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetNotificationPreference(account models.Account) (models.PreferenceNotification, error) {
|
func GetNotifyPreference(account models.Account) (models.PreferenceNotification, error) {
|
||||||
var notification models.PreferenceNotification
|
var notification models.PreferenceNotification
|
||||||
cacheManager := cache.New[any](localCache.S)
|
if val, err := cachekit.Get[models.PreferenceNotification](
|
||||||
marshal := marshaler.New(cacheManager)
|
gap.Ca,
|
||||||
contx := context.Background()
|
KgNotifyPreferenceCache(account.ID),
|
||||||
|
); err == nil {
|
||||||
if val, err := marshal.Get(contx, GetNotificationPreferenceCacheKey(account.ID), new(models.PreferenceNotification)); err == nil {
|
return val, nil
|
||||||
notification = val.(models.PreferenceNotification)
|
}
|
||||||
} else {
|
|
||||||
if err := database.C.Where("account_id = ?", account.ID).First(¬ification).Error; err != nil {
|
if err := database.C.Where("account_id = ?", account.ID).First(¬ification).Error; err != nil {
|
||||||
return notification, err
|
return notification, err
|
||||||
}
|
}
|
||||||
CacheNotificationPreference(notification)
|
CacheNotifyPreference(notification)
|
||||||
}
|
|
||||||
|
|
||||||
return notification, nil
|
return notification, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CacheNotificationPreference(prefs models.PreferenceNotification) {
|
func CacheNotifyPreference(prefs models.PreferenceNotification) {
|
||||||
cacheManager := cache.New[any](localCache.S)
|
cachekit.Set[models.PreferenceNotification](
|
||||||
marshal := marshaler.New(cacheManager)
|
gap.Ca,
|
||||||
contx := context.Background()
|
KgNotifyPreferenceCache(prefs.AccountID),
|
||||||
|
|
||||||
_ = marshal.Set(
|
|
||||||
contx,
|
|
||||||
GetNotificationPreferenceCacheKey(prefs.AccountID),
|
|
||||||
prefs,
|
prefs,
|
||||||
store.WithExpiration(60*time.Minute),
|
time.Minute*60,
|
||||||
store.WithTags([]string{"notification-preference", fmt.Sprintf("user#%d", prefs.AccountID)}),
|
fmt.Sprintf("user#%d", prefs.AccountID),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func UpdateNotificationPreference(account models.Account, config map[string]bool) (models.PreferenceNotification, error) {
|
func UpdateNotifyPreference(account models.Account, config map[string]bool) (models.PreferenceNotification, error) {
|
||||||
var notification models.PreferenceNotification
|
var notification models.PreferenceNotification
|
||||||
var err error
|
var err error
|
||||||
if notification, err = GetNotificationPreference(account); err != nil {
|
if notification, err = GetNotifyPreference(account); err != nil {
|
||||||
notification = models.PreferenceNotification{
|
notification = models.PreferenceNotification{
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
Config: lo.MapValues(config, func(v bool, k string) any { return v }),
|
Config: lo.MapValues(config, func(v bool, k string) any { return v }),
|
||||||
@ -91,7 +82,7 @@ func UpdateNotificationPreference(account models.Account, config map[string]bool
|
|||||||
|
|
||||||
err = database.C.Save(¬ification).Error
|
err = database.C.Save(¬ification).Error
|
||||||
if err == nil {
|
if err == nil {
|
||||||
CacheNotificationPreference(notification)
|
CacheNotifyPreference(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
return notification, err
|
return notification, err
|
||||||
@ -99,17 +90,16 @@ func UpdateNotificationPreference(account models.Account, config map[string]bool
|
|||||||
|
|
||||||
func CheckNotificationNotifiable(account models.Account, topic string) bool {
|
func CheckNotificationNotifiable(account models.Account, topic string) bool {
|
||||||
var notification models.PreferenceNotification
|
var notification models.PreferenceNotification
|
||||||
cacheManager := cache.New[any](localCache.S)
|
if val, err := cachekit.Get[models.PreferenceNotification](
|
||||||
marshal := marshaler.New(cacheManager)
|
gap.Ca,
|
||||||
contx := context.Background()
|
KgNotifyPreferenceCache(account.ID),
|
||||||
|
); err == nil {
|
||||||
if val, err := marshal.Get(contx, GetNotificationPreferenceCacheKey(account.ID), new(models.PreferenceNotification)); err == nil {
|
notification = val
|
||||||
notification = val.(models.PreferenceNotification)
|
|
||||||
} else {
|
} else {
|
||||||
if err := database.C.Where("account_id = ?", account.ID).First(¬ification).Error; err != nil {
|
if err := database.C.Where("account_id = ?", account.ID).First(¬ification).Error; err != nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
CacheNotificationPreference(notification)
|
CacheNotifyPreference(notification)
|
||||||
}
|
}
|
||||||
|
|
||||||
if val, ok := notification.Config[topic]; ok {
|
if val, ok := notification.Config[topic]; ok {
|
||||||
@ -121,20 +111,15 @@ func CheckNotificationNotifiable(account models.Account, topic string) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CheckNotificationNotifiableBatch(accounts []models.Account, topic string) []bool {
|
func CheckNotificationNotifiableBatch(accounts []models.Account, topic string) []bool {
|
||||||
cacheManager := cache.New[any](localCache.S)
|
notifiable := make([]bool, len(accounts))
|
||||||
marshal := marshaler.New(cacheManager)
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
var notifiable = make([]bool, len(accounts))
|
|
||||||
var queryNeededIdx []uint
|
var queryNeededIdx []uint
|
||||||
notificationMap := make(map[uint]models.PreferenceNotification)
|
notificationMap := make(map[uint]models.PreferenceNotification)
|
||||||
|
|
||||||
// Check cache for each account
|
// Check cache for each account
|
||||||
for _, account := range accounts {
|
for _, account := range accounts {
|
||||||
cacheKey := GetNotificationPreferenceCacheKey(account.ID)
|
cacheKey := KgNotifyPreferenceCache(account.ID)
|
||||||
if val, err := marshal.Get(ctx, cacheKey, new(models.PreferenceNotification)); err == nil {
|
if val, err := cachekit.Get[models.PreferenceNotification](gap.Ca, cacheKey); err == nil {
|
||||||
notification := val.(*models.PreferenceNotification)
|
notificationMap[account.ID] = val
|
||||||
notificationMap[account.ID] = *notification
|
|
||||||
} else {
|
} else {
|
||||||
// Add to the list of accounts that need to be queried
|
// Add to the list of accounts that need to be queried
|
||||||
queryNeededIdx = append(queryNeededIdx, account.ID)
|
queryNeededIdx = append(queryNeededIdx, account.ID)
|
||||||
@ -154,7 +139,7 @@ func CheckNotificationNotifiableBatch(accounts []models.Account, topic string) [
|
|||||||
// Cache the newly fetched notifications and add to the notificationMap
|
// Cache the newly fetched notifications and add to the notificationMap
|
||||||
for _, notification := range dbNotifications {
|
for _, notification := range dbNotifications {
|
||||||
notificationMap[notification.AccountID] = notification
|
notificationMap[notification.AccountID] = notification
|
||||||
CacheNotificationPreference(notification) // Cache the result
|
CacheNotifyPreference(notification) // Cache the result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
142
pkg/internal/services/programs.go
Normal file
142
pkg/internal/services/programs.go
Normal file
@ -0,0 +1,142 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/wallet/pkg/proto"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
)
|
||||||
|
|
||||||
|
func JoinProgram(user models.Account, program models.Program) (models.ProgramMember, error) {
|
||||||
|
var member models.ProgramMember
|
||||||
|
if err := database.C.Where("account_id = ? AND program_id = ?", user.ID, program.ID).First(&member).Error; err == nil {
|
||||||
|
return member, fmt.Errorf("program member already exists")
|
||||||
|
}
|
||||||
|
var profile models.AccountProfile
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).Select("experience").First(&profile).Error; err != nil {
|
||||||
|
return member, err
|
||||||
|
}
|
||||||
|
if program.ExpRequirement > int64(profile.Experience) {
|
||||||
|
return member, fmt.Errorf("insufficient experience")
|
||||||
|
}
|
||||||
|
member = models.ProgramMember{
|
||||||
|
LastPaid: lo.ToPtr(time.Now()),
|
||||||
|
Account: user,
|
||||||
|
AccountID: user.ID,
|
||||||
|
Program: program,
|
||||||
|
ProgramID: program.ID,
|
||||||
|
}
|
||||||
|
if err := ChargeForProgram(member); err != nil {
|
||||||
|
return member, err
|
||||||
|
}
|
||||||
|
if err := database.C.Create(&member).Error; err != nil {
|
||||||
|
return member, err
|
||||||
|
} else {
|
||||||
|
PostJoinProgram(member)
|
||||||
|
}
|
||||||
|
return member, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func LeaveProgram(user models.Account, program models.Program) error {
|
||||||
|
var member models.ProgramMember
|
||||||
|
if err := database.C.Where("account_id = ? AND program_id = ?", user.ID, program.ID).
|
||||||
|
Preload("Program").
|
||||||
|
First(&member).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := database.C.Delete(&member).Error; err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
PostLeaveProgram(member)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ChargeForProgram(member models.ProgramMember) error {
|
||||||
|
pricing := member.Program.Price.Data()
|
||||||
|
if pricing.Amount == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
conn, err := gap.Nx.GetClientGrpcConn("wa")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
wc := proto.NewPaymentServiceClient(conn)
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
|
||||||
|
defer cancel()
|
||||||
|
_, err = wc.MakeTransactionWithAccount(ctx, &proto.MakeTransactionWithAccountRequest{
|
||||||
|
PayerAccountId: lo.ToPtr(uint64(member.AccountID)),
|
||||||
|
Amount: pricing.Amount,
|
||||||
|
Currency: pricing.Currency,
|
||||||
|
Remark: fmt.Sprintf("Program Membership: %s", member.Program.Name),
|
||||||
|
})
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func PeriodicChargeProgramFee() {
|
||||||
|
var members []models.ProgramMember
|
||||||
|
// Every month paid once
|
||||||
|
if err := database.C.Where("last_paid IS NULL OR last_paid < ?", time.Now().AddDate(0, 0, -30)).
|
||||||
|
Preload("Program").Preload("Account").Find(&members).Error; err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, member := range members {
|
||||||
|
if err := ChargeForProgram(member); err == nil {
|
||||||
|
database.C.Model(&member).Update("last_paid", time.Now())
|
||||||
|
} else {
|
||||||
|
LeaveProgram(member.Account, member.Program)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostJoinProgram(member models.ProgramMember) error {
|
||||||
|
badge := member.Program.Badge.Data()
|
||||||
|
if len(badge.Type) > 0 {
|
||||||
|
accountBadge := models.Badge{
|
||||||
|
Type: badge.Type,
|
||||||
|
AccountID: member.AccountID,
|
||||||
|
Metadata: datatypes.JSONMap(badge.Metadata),
|
||||||
|
}
|
||||||
|
if err := database.C.Create(&accountBadge).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to create badge for program member...")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group := member.Program.Group.Data()
|
||||||
|
if group.ID > 0 {
|
||||||
|
accountGroup := models.AccountGroupMember{
|
||||||
|
GroupID: group.ID,
|
||||||
|
AccountID: member.AccountID,
|
||||||
|
}
|
||||||
|
if err := database.C.Create(&accountGroup).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to create group for program member...")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PostLeaveProgram(member models.ProgramMember) error {
|
||||||
|
badge := member.Program.Badge.Data()
|
||||||
|
if len(badge.Type) > 0 {
|
||||||
|
if err := database.C.Where("account_id = ? AND type = ?", member.AccountID, badge.Type).Delete(&models.Badge{}).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to delete badge for program member...")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
group := member.Program.Group.Data()
|
||||||
|
if group.ID > 0 {
|
||||||
|
if err := database.C.Where("account_id = ? AND group_id = ?", member.AccountID, group.ID).Delete(&models.AccountGroupMember{}).Error; err != nil {
|
||||||
|
log.Error().Err(err).Msg("Failed to delete group for program member...")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
203
pkg/internal/services/punishments.go
Normal file
203
pkg/internal/services/punishments.go
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
package services
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"github.com/rs/zerolog/log"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewPunishment(in models.Punishment, moderator ...models.Account) (models.Punishment, error) {
|
||||||
|
if len(moderator) > 0 {
|
||||||
|
in.Moderator = &moderator[0]
|
||||||
|
in.ModeratorID = &moderator[0].ID
|
||||||
|
}
|
||||||
|
|
||||||
|
// If user got more than 2 strikes, it will upgrade to limited
|
||||||
|
if in.Type == models.PunishmentTypeStrike {
|
||||||
|
var count int64
|
||||||
|
if err := database.C.Model(&models.Punishment{}).
|
||||||
|
Where("account_id = ? AND type = ?", in.AccountID, models.PunishmentTypeStrike).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return in, err
|
||||||
|
}
|
||||||
|
if count > 2 {
|
||||||
|
in.Type = models.PunishmentTypeLimited
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Create(&in).Error; err != nil {
|
||||||
|
return in, err
|
||||||
|
} else {
|
||||||
|
moderator := "System"
|
||||||
|
if in.Moderator != nil {
|
||||||
|
moderator = fmt.Sprintf("@%s", in.Moderator.Name)
|
||||||
|
}
|
||||||
|
err = NewNotification(models.Notification{
|
||||||
|
Topic: "passport.punishments",
|
||||||
|
Title: localize.L.GetLocalizedString("subjectPunishmentCreated", in.Account.Language),
|
||||||
|
Subtitle: fmt.Sprintf(localize.L.GetLocalizedString("subtitlePunishment", in.Account.Language), in.ID, moderator),
|
||||||
|
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyPunishmentCreated", in.Account.Language), in.Reason),
|
||||||
|
Account: in.Account,
|
||||||
|
AccountID: in.Account.ID,
|
||||||
|
Metadata: map[string]any{"punishment": in},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Uint("case", in.ID).Msg("Failed to delivery punishment via notify...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func EditPunishment(in models.Punishment) (models.Punishment, error) {
|
||||||
|
if err := database.C.Save(&in).Error; err != nil {
|
||||||
|
return in, err
|
||||||
|
} else {
|
||||||
|
moderator := "System"
|
||||||
|
if in.Moderator != nil {
|
||||||
|
moderator = fmt.Sprintf("@%s", in.Moderator.Name)
|
||||||
|
}
|
||||||
|
err = NewNotification(models.Notification{
|
||||||
|
Topic: "passport.punishments",
|
||||||
|
Title: localize.L.GetLocalizedString("subjectPunishmentUpdated", in.Account.Language),
|
||||||
|
Subtitle: fmt.Sprintf(localize.L.GetLocalizedString("subtitlePunishment", in.Account.Language), in.ID, moderator),
|
||||||
|
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyPunishmentUpdated", in.Account.Language), in.ID),
|
||||||
|
Account: in.Account,
|
||||||
|
AccountID: in.Account.ID,
|
||||||
|
Metadata: map[string]any{"punishment": in},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Uint("case", in.ID).Msg("Failed to delivery punishment via notify...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return in, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeletePunishment(in models.Punishment) error {
|
||||||
|
if err := database.C.Delete(&in).Error; err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
moderator := "System"
|
||||||
|
if in.Moderator != nil {
|
||||||
|
moderator = fmt.Sprintf("@%s", in.Moderator.Name)
|
||||||
|
}
|
||||||
|
err = NewNotification(models.Notification{
|
||||||
|
Topic: "passport.punishments",
|
||||||
|
Title: localize.L.GetLocalizedString("subjectPunishmentDeleted", in.Account.Language),
|
||||||
|
Subtitle: fmt.Sprintf(localize.L.GetLocalizedString("subtitlePunishment", in.Account.Language), in.ID, moderator),
|
||||||
|
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyPunishmentDeleted", in.Account.Language), in.ID),
|
||||||
|
Account: in.Account,
|
||||||
|
AccountID: in.Account.ID,
|
||||||
|
Metadata: map[string]any{"punishment": in},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Warn().Err(err).Uint("case", in.ID).Msg("Failed to delivery punishment via notify...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPunishment(id uint, preload ...bool) (models.Punishment, error) {
|
||||||
|
tx := database.C
|
||||||
|
if len(preload) > 0 && preload[0] {
|
||||||
|
tx = tx.Preload("Moderator").Preload("Account")
|
||||||
|
}
|
||||||
|
|
||||||
|
var punishment models.Punishment
|
||||||
|
if err := tx.First(&punishment, id).Error; err != nil {
|
||||||
|
return punishment, err
|
||||||
|
}
|
||||||
|
return punishment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetMadePunishment(id uint, moderator models.Account) (models.Punishment, error) {
|
||||||
|
var punishment models.Punishment
|
||||||
|
if err := database.C.Where("id = ? AND moderator_id = ?", id, moderator.ID).First(&punishment).Error; err != nil {
|
||||||
|
return punishment, err
|
||||||
|
}
|
||||||
|
return punishment, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListPunishments(user models.Account) ([]models.Punishment, error) {
|
||||||
|
var punishments []models.Punishment
|
||||||
|
if err := database.C.
|
||||||
|
Where("account_id = ? AND (expired_at IS NULL OR expired_at > ?)", user.ID, time.Now()).
|
||||||
|
Preload("Moderator").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Find(&punishments).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return punishments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountAllPunishments() (int64, error) {
|
||||||
|
var count int64
|
||||||
|
if err := database.C.
|
||||||
|
Model(&models.Punishment{}).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListAllPunishments(take, offset int) ([]models.Punishment, error) {
|
||||||
|
var punishments []models.Punishment
|
||||||
|
if err := database.C.
|
||||||
|
Preload("Account").
|
||||||
|
Preload("Moderator").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Take(take).Offset(offset).
|
||||||
|
Find(&punishments).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return punishments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CountMadePunishments(moderator models.Account) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
if err := database.C.
|
||||||
|
Model(&models.Punishment{}).
|
||||||
|
Where("moderator_id = ?", moderator.ID).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListMadePunishments(moderator models.Account, take, offset int) ([]models.Punishment, error) {
|
||||||
|
var punishments []models.Punishment
|
||||||
|
if err := database.C.
|
||||||
|
Where("moderator_id = ?", moderator.ID).
|
||||||
|
Preload("Account").
|
||||||
|
Order("created_at DESC").
|
||||||
|
Take(take).Offset(offset).
|
||||||
|
Find(&punishments).Error; err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return punishments, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckLoginAbility(user models.Account) error {
|
||||||
|
var punishments []models.Punishment
|
||||||
|
if err := database.C.Where("account_id = ? AND (expired_at IS NULL OR expired_at > ?)", user.ID, time.Now()).
|
||||||
|
Find(&punishments).Error; err != nil {
|
||||||
|
return fmt.Errorf("failed to get punishments: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, punishment := range punishments {
|
||||||
|
if punishment.Type == models.PunishmentTypeDisabled {
|
||||||
|
return fmt.Errorf("account has been fully disabled due to: %s (case #%d)", punishment.Reason, punishment.ID)
|
||||||
|
}
|
||||||
|
// Limited punishment with no permissions override is fully limited
|
||||||
|
// Refer https://solsynth.dev/terms/basic-law#provision-and-discontinuation-of-services
|
||||||
|
if punishment.Type == models.PunishmentTypeLimited && len(punishment.PermNodes) == 0 {
|
||||||
|
return fmt.Errorf("account has been limited login due to: %s (case #%d)", punishment.Reason, punishment.ID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -1,18 +1,27 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
"git.solsynth.dev/hypernet/paperclip/pkg/filekit"
|
||||||
|
pproto "git.solsynth.dev/hypernet/paperclip/pkg/proto"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"strconv"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListCommunityRealm() ([]models.Realm, error) {
|
func ListCommunityRealm() ([]models.Realm, error) {
|
||||||
var realms []models.Realm
|
var realms []models.Realm
|
||||||
if err := database.C.Where(&models.Realm{
|
if err := database.C.Where(&models.Realm{
|
||||||
IsCommunity: true,
|
IsCommunity: true,
|
||||||
}).Find(&realms).Error; err != nil {
|
}).Order("popularity DESC").Find(&realms).Error; err != nil {
|
||||||
return realms, err
|
return realms, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -28,7 +37,7 @@ func ListOwnedRealm(user models.Account) ([]models.Realm, error) {
|
|||||||
return realms, nil
|
return realms, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListAvailableRealm(user models.Account) ([]models.Realm, error) {
|
func ListAvailableRealm(user models.Account, includePublic ...bool) ([]models.Realm, error) {
|
||||||
var realms []models.Realm
|
var realms []models.Realm
|
||||||
var members []models.RealmMember
|
var members []models.RealmMember
|
||||||
if err := database.C.Where(&models.RealmMember{
|
if err := database.C.Where(&models.RealmMember{
|
||||||
@ -41,7 +50,12 @@ func ListAvailableRealm(user models.Account) ([]models.Realm, error) {
|
|||||||
return item.RealmID
|
return item.RealmID
|
||||||
})
|
})
|
||||||
|
|
||||||
if err := database.C.Where("id IN ?", idx).Find(&realms).Error; err != nil {
|
tx := database.C
|
||||||
|
if len(includePublic) > 0 && includePublic[0] {
|
||||||
|
tx = tx.Where("is_public = ?", true)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := tx.Where("id IN ?", idx).Find(&realms).Error; err != nil {
|
||||||
return realms, err
|
return realms, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,14 +82,40 @@ func NewRealm(realm models.Realm, user models.Account) (models.Realm, error) {
|
|||||||
{AccountID: user.ID, PowerLevel: 100},
|
{AccountID: user.ID, PowerLevel: 100},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var attachments []string
|
||||||
|
if realm.Avatar != nil && len(*realm.Avatar) > 0 {
|
||||||
|
attachments = append(attachments, *realm.Avatar)
|
||||||
|
}
|
||||||
|
if realm.Banner != nil && len(*realm.Banner) > 0 {
|
||||||
|
attachments = append(attachments, *realm.Banner)
|
||||||
|
}
|
||||||
|
if len(attachments) > 0 {
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &pproto.UpdateUsageRequest{
|
||||||
|
Rid: attachments,
|
||||||
|
Delta: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
err := database.C.Save(&realm).Error
|
err := database.C.Save(&realm).Error
|
||||||
return realm, err
|
return realm, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListRealmMember(realmId uint) ([]models.RealmMember, error) {
|
func CountRealmMember(realmId uint) (int64, error) {
|
||||||
|
var count int64
|
||||||
|
if err := database.C.Where(&models.RealmMember{
|
||||||
|
RealmID: realmId,
|
||||||
|
}).Model(&models.RealmMember{}).Count(&count).Error; err != nil {
|
||||||
|
return 0, err
|
||||||
|
} else {
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func ListRealmMember(realmId uint, take int, offset int) ([]models.RealmMember, error) {
|
||||||
var members []models.RealmMember
|
var members []models.RealmMember
|
||||||
|
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
|
Limit(take).Offset(offset).
|
||||||
Where(&models.RealmMember{RealmID: realmId}).
|
Where(&models.RealmMember{RealmID: realmId}).
|
||||||
Preload("Account").
|
Preload("Account").
|
||||||
Find(&members).Error; err != nil {
|
Find(&members).Error; err != nil {
|
||||||
@ -97,11 +137,19 @@ func GetRealmMember(userId uint, realmId uint) (models.RealmMember, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddRealmMember(user models.Account, affected models.Account, target models.Realm) error {
|
func AddRealmMember(user models.Account, affected models.Account, target models.Realm) error {
|
||||||
if !target.IsPublic && !target.IsCommunity {
|
var member models.RealmMember
|
||||||
|
if err := database.C.Where(&models.RealmMember{
|
||||||
|
AccountID: affected.ID,
|
||||||
|
RealmID: target.ID,
|
||||||
|
}).First(&member).Error; err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if !target.IsCommunity {
|
||||||
if member, err := GetRealmMember(user.ID, target.ID); err != nil {
|
if member, err := GetRealmMember(user.ID, target.ID); err != nil {
|
||||||
return fmt.Errorf("only realm member can add people: %v", err)
|
return fmt.Errorf("only realm member can add people: %v", err)
|
||||||
} else if member.PowerLevel < 50 {
|
} else if member.PowerLevel < 50 {
|
||||||
return fmt.Errorf("only realm moderator can add people")
|
return fmt.Errorf("only realm moderator can add member")
|
||||||
}
|
}
|
||||||
rel, err := GetRelationWithTwoNode(affected.ID, user.ID)
|
rel, err := GetRelationWithTwoNode(affected.ID, user.ID)
|
||||||
if err != nil || HasPermNodeWithDefault(
|
if err != nil || HasPermNodeWithDefault(
|
||||||
@ -114,39 +162,104 @@ func AddRealmMember(user models.Account, affected models.Account, target models.
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
member := models.RealmMember{
|
member = models.RealmMember{
|
||||||
RealmID: target.ID,
|
RealmID: target.ID,
|
||||||
AccountID: affected.ID,
|
AccountID: affected.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := database.C.Save(&member).Error
|
err := database.C.Save(&member).Error
|
||||||
|
if err == nil {
|
||||||
|
database.C.Model(&models.Realm{}).
|
||||||
|
Where("id = ?", target.ID).
|
||||||
|
Update("popularity", gorm.Expr("popularity + ?", models.RealmPopularityMemberFactor))
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveRealmMember(user models.Account, affected models.Account, target models.Realm) error {
|
func RemoveRealmMember(user models.Account, affected models.RealmMember, target models.Realm) error {
|
||||||
if user.ID != affected.ID {
|
if user.ID != affected.AccountID {
|
||||||
if member, err := GetRealmMember(user.ID, target.ID); err != nil {
|
if member, err := GetRealmMember(user.ID, target.ID); err != nil {
|
||||||
return fmt.Errorf("only realm member can remove other member: %v", err)
|
return fmt.Errorf("only realm member can remove other member: %v", err)
|
||||||
} else if member.PowerLevel < 50 {
|
} else if member.PowerLevel < 50 {
|
||||||
return fmt.Errorf("only realm moderator can invite people")
|
return fmt.Errorf("only realm moderator can kick member")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var member models.RealmMember
|
if err := database.C.Delete(&affected).Error; err != nil {
|
||||||
if err := database.C.Where(&models.RealmMember{
|
|
||||||
RealmID: target.ID,
|
|
||||||
AccountID: affected.ID,
|
|
||||||
}).First(&member).Error; err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return database.C.Delete(&member).Error
|
database.C.Model(&models.Realm{}).
|
||||||
|
Where("id = ?", target.ID).
|
||||||
|
Update("popularity", gorm.Expr("popularity - ?", models.RealmPopularityMemberFactor))
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func EditRealm(realm models.Realm) (models.Realm, error) {
|
func EditRealm(realm, og models.Realm) (models.Realm, error) {
|
||||||
err := database.C.Save(&realm).Error
|
err := database.C.Save(&realm).Error
|
||||||
|
if err == nil {
|
||||||
|
var minusAttachments, plusAttachments []string
|
||||||
|
if realm.Avatar != og.Avatar && realm.Avatar != nil {
|
||||||
|
minusAttachments = append(minusAttachments, *og.Avatar)
|
||||||
|
plusAttachments = append(plusAttachments, *realm.Avatar)
|
||||||
|
}
|
||||||
|
if realm.Banner != og.Banner && realm.Banner != nil {
|
||||||
|
minusAttachments = append(minusAttachments, *og.Banner)
|
||||||
|
plusAttachments = append(plusAttachments, *realm.Banner)
|
||||||
|
}
|
||||||
|
if len(minusAttachments) > 0 {
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &pproto.UpdateUsageRequest{
|
||||||
|
Rid: minusAttachments,
|
||||||
|
Delta: -1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if len(plusAttachments) > 0 {
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &pproto.UpdateUsageRequest{
|
||||||
|
Rid: plusAttachments,
|
||||||
|
Delta: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
return realm, err
|
return realm, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func DeleteRealm(realm models.Realm) error {
|
func DeleteRealm(realm models.Realm) error {
|
||||||
return database.C.Delete(&realm).Error
|
tx := database.C.Begin()
|
||||||
|
if err := tx.Where("realm_id = ?", realm.ID).Delete(&models.RealmMember{}).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Delete(&realm).Error; err != nil {
|
||||||
|
tx.Rollback()
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := tx.Commit().Error; err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
var attachments []string
|
||||||
|
if realm.Avatar != nil && len(*realm.Avatar) > 0 {
|
||||||
|
attachments = append(attachments, *realm.Avatar)
|
||||||
|
}
|
||||||
|
if realm.Banner != nil && len(*realm.Banner) > 0 {
|
||||||
|
attachments = append(attachments, *realm.Banner)
|
||||||
|
}
|
||||||
|
if len(attachments) > 0 {
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &pproto.UpdateUsageRequest{
|
||||||
|
Rid: attachments,
|
||||||
|
Delta: -1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
conn := gap.Nx.GetNexusGrpcConn()
|
||||||
|
_, _ = proto.NewDirectoryServiceClient(conn).BroadcastEvent(context.Background(), &proto.EventInfo{
|
||||||
|
Event: "deletion",
|
||||||
|
Data: nex.EncodeMap(map[string]any{
|
||||||
|
"type": "realm",
|
||||||
|
"id": realm.ID,
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
package services
|
package services
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
@ -22,10 +22,10 @@ func ListAllRelationship(user models.Account) ([]models.AccountRelationship, err
|
|||||||
return relationships, nil
|
return relationships, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListRelationshipWithFilter(user models.Account, status models.RelationshipStatus) ([]models.AccountRelationship, error) {
|
func ListRelationshipWithFilter(user models.Account, status ...models.RelationshipStatus) ([]models.AccountRelationship, error) {
|
||||||
var relationships []models.AccountRelationship
|
var relationships []models.AccountRelationship
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where("account_id = ? AND status = ?", user.ID, status).
|
Where("account_id = ? AND status IN ?", user.ID, status).
|
||||||
Preload("Account").
|
Preload("Account").
|
||||||
Preload("Related").
|
Preload("Related").
|
||||||
Find(&relationships).Error; err != nil {
|
Find(&relationships).Error; err != nil {
|
||||||
@ -112,17 +112,29 @@ func NewFriend(userA models.Account, userB models.Account, skipPending ...bool)
|
|||||||
Status: models.RelationshipPending,
|
Status: models.RelationshipPending,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if userA.ID == userB.ID {
|
||||||
|
return relA, fmt.Errorf("unable to make relationship with yourself")
|
||||||
|
}
|
||||||
|
|
||||||
|
var dupeCount int
|
||||||
|
if rel, err := GetRelationWithTwoNode(userA.ID, userB.ID, true); err == nil {
|
||||||
|
relA = rel
|
||||||
|
dupeCount++
|
||||||
|
}
|
||||||
|
if rel, err := GetRelationWithTwoNode(userB.ID, userA.ID, true); err == nil {
|
||||||
|
relB = rel
|
||||||
|
dupeCount++
|
||||||
|
}
|
||||||
|
|
||||||
|
if dupeCount > 1 {
|
||||||
|
return relA, fmt.Errorf("unable to recreate a relationship with that user")
|
||||||
|
}
|
||||||
|
|
||||||
if len(skipPending) > 0 && skipPending[0] {
|
if len(skipPending) > 0 && skipPending[0] {
|
||||||
relA.Status = models.RelationshipFriend
|
relA.Status = models.RelationshipFriend
|
||||||
relB.Status = models.RelationshipFriend
|
relB.Status = models.RelationshipFriend
|
||||||
}
|
}
|
||||||
|
|
||||||
if userA.ID == userB.ID {
|
|
||||||
return relA, fmt.Errorf("unable to make relationship with yourself")
|
|
||||||
} else if _, err := GetRelationWithTwoNode(userA.ID, userB.ID, true); err == nil || !errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return relA, fmt.Errorf("unable to recreate a relationship with that user")
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.C.Save(&relA).Error; err != nil {
|
if err := database.C.Save(&relA).Error; err != nil {
|
||||||
return relA, err
|
return relA, err
|
||||||
} else if err = database.C.Save(&relB).Error; err != nil {
|
} else if err = database.C.Save(&relB).Error; err != nil {
|
||||||
|
@ -2,6 +2,7 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
)
|
)
|
||||||
@ -42,9 +43,8 @@ func UpdateAbuseReportStatus(id uint, status, message string) error {
|
|||||||
|
|
||||||
_ = NewNotification(models.Notification{
|
_ = NewNotification(models.Notification{
|
||||||
Topic: "reports.feedback",
|
Topic: "reports.feedback",
|
||||||
Title: "Abuse report status has been changed.",
|
Title: localize.L.GetLocalizedString("subjectAbuseReportUpdated", account.Language),
|
||||||
Subtitle: fmt.Sprintf("The report #%d's status updated", id),
|
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyAbuseReportUpdated", account.Language), id, status, message),
|
||||||
Body: fmt.Sprintf("The report created by you with ID #%d's status has been changed to %s. Moderator message: %s", id, status, message),
|
|
||||||
Account: account,
|
Account: account,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
})
|
})
|
||||||
@ -67,6 +67,7 @@ func NewAbuseReport(resource string, reason string, account models.Account) (mod
|
|||||||
report = models.AbuseReport{
|
report = models.AbuseReport{
|
||||||
Resource: resource,
|
Resource: resource,
|
||||||
Reason: reason,
|
Reason: reason,
|
||||||
|
Status: models.ReportStatusPending,
|
||||||
AccountID: account.ID,
|
AccountID: account.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,62 +0,0 @@
|
|||||||
package services
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
|
||||||
"gorm.io/gorm"
|
|
||||||
"math"
|
|
||||||
"math/rand"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
func CheckDailyCanSign(user models.Account) error {
|
|
||||||
probe := time.Now().Format("2006-01-02")
|
|
||||||
|
|
||||||
var record models.SignRecord
|
|
||||||
if err := database.C.Where("account_id = ? AND created_at::date = ?", user.ID, probe).First(&record).Error; err != nil {
|
|
||||||
if errors.Is(err, gorm.ErrRecordNotFound) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return fmt.Errorf("unable check daliy sign record: %v", err)
|
|
||||||
}
|
|
||||||
return fmt.Errorf("daliy sign record exists")
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetTodayDailySign(user models.Account) (models.SignRecord, error) {
|
|
||||||
probe := time.Now().Format("2006-01-02")
|
|
||||||
|
|
||||||
var record models.SignRecord
|
|
||||||
if err := database.C.Where("account_id = ? AND created_at::date = ?", user.ID, probe).First(&record).Error; err != nil {
|
|
||||||
return record, fmt.Errorf("unable get daliy sign record: %v", err)
|
|
||||||
}
|
|
||||||
return record, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func DailySign(user models.Account) (models.SignRecord, error) {
|
|
||||||
tier := rand.Intn(5)
|
|
||||||
record := models.SignRecord{
|
|
||||||
ResultTier: tier,
|
|
||||||
ResultExperience: rand.Intn(int(math.Max(float64(tier), 1)*100)+1-100) + 100,
|
|
||||||
AccountID: user.ID,
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := CheckDailyCanSign(user); err != nil {
|
|
||||||
return record, fmt.Errorf("today already signed")
|
|
||||||
}
|
|
||||||
|
|
||||||
var profile models.AccountProfile
|
|
||||||
if err := database.C.Where("account_id = ?", user.ID).First(&profile).Error; err != nil {
|
|
||||||
return record, fmt.Errorf("unable get account profile: %v", err)
|
|
||||||
} else {
|
|
||||||
profile.Experience += uint64(record.ResultExperience)
|
|
||||||
database.C.Save(&profile)
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.C.Save(&record).Error; err != nil {
|
|
||||||
return record, fmt.Errorf("unable do daliy sign: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return record, nil
|
|
||||||
}
|
|
@ -3,36 +3,25 @@ package services
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
localCache "git.solsynth.dev/hypernet/passport/pkg/internal/cache"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/proto"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/eko/gocache/lib/v4/cache"
|
|
||||||
"github.com/eko/gocache/lib/v4/marshaler"
|
|
||||||
"github.com/eko/gocache/lib/v4/store"
|
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetStatusCacheKey(uid uint) string {
|
func KgStatusCache(uid uint) string {
|
||||||
return fmt.Sprintf("user-status#%d", uid)
|
return fmt.Sprintf("user-status#%d", uid)
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetStatus(uid uint) (models.Status, error) {
|
func GetStatus(uid uint) (models.Status, error) {
|
||||||
cacheManager := cache.New[any](localCache.S)
|
if val, err := cachekit.Get[models.Status](gap.Ca, KgStatusCache(uid)); err == nil {
|
||||||
marshal := marshaler.New(cacheManager)
|
return val, nil
|
||||||
contx := context.Background()
|
|
||||||
|
|
||||||
if val, err := marshal.Get(contx, GetStatusCacheKey(uid), new(models.Status)); err == nil {
|
|
||||||
status := val.(models.Status)
|
|
||||||
if status.ClearAt != nil && status.ClearAt.Before(time.Now()) {
|
|
||||||
marshal.Delete(contx, GetStatusCacheKey(uid))
|
|
||||||
} else {
|
|
||||||
return status, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
var status models.Status
|
var status models.Status
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
@ -47,15 +36,12 @@ func GetStatus(uid uint) (models.Status, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func CacheUserStatus(uid uint, status models.Status) {
|
func CacheUserStatus(uid uint, status models.Status) {
|
||||||
cacheManager := cache.New[any](localCache.S)
|
cachekit.Set[models.Status](
|
||||||
marshal := marshaler.New(cacheManager)
|
gap.Ca,
|
||||||
contx := context.Background()
|
KgStatusCache(uid),
|
||||||
|
|
||||||
marshal.Set(
|
|
||||||
contx,
|
|
||||||
GetStatusCacheKey(uid),
|
|
||||||
status,
|
status,
|
||||||
store.WithTags([]string{"user-status", fmt.Sprintf("user#%d", uid)}),
|
time.Minute*5,
|
||||||
|
fmt.Sprintf("user#%d", uid),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -123,11 +109,7 @@ func ClearStatus(user models.Account) error {
|
|||||||
Updates(models.Status{ClearAt: lo.ToPtr(time.Now())}).Error; err != nil {
|
Updates(models.Status{ClearAt: lo.ToPtr(time.Now())}).Error; err != nil {
|
||||||
return err
|
return err
|
||||||
} else {
|
} else {
|
||||||
cacheManager := cache.New[any](localCache.S)
|
cachekit.Delete(gap.Ca, KgStatusCache(user.ID))
|
||||||
marshal := marshaler.New(cacheManager)
|
|
||||||
contx := context.Background()
|
|
||||||
|
|
||||||
marshal.Delete(contx, GetStatusCacheKey(user.ID))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -2,11 +2,17 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"net"
|
||||||
"gorm.io/datatypes"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"gorm.io/datatypes"
|
||||||
|
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
|
|
||||||
@ -60,22 +66,41 @@ func NewTicket(user models.Account, ip, ua string) (models.AuthTicket, error) {
|
|||||||
cfg, err := GetAuthPreference(user)
|
cfg, err := GetAuthPreference(user)
|
||||||
if err == nil && cfg.Config.Data().MaximumAuthSteps >= 1 {
|
if err == nil && cfg.Config.Data().MaximumAuthSteps >= 1 {
|
||||||
steps = min(steps, cfg.Config.Data().MaximumAuthSteps)
|
steps = min(steps, cfg.Config.Data().MaximumAuthSteps)
|
||||||
|
} else {
|
||||||
|
steps = min(steps, 2)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var location *string
|
||||||
|
var coordinateX, coordinateY *float64
|
||||||
|
netIp := net.ParseIP(ip)
|
||||||
|
record, err := database.Gc.City(netIp)
|
||||||
|
if err == nil {
|
||||||
|
var locationNames []string
|
||||||
|
locationNames = append(locationNames, record.City.Names["en"])
|
||||||
|
for _, subs := range record.Subdivisions {
|
||||||
|
locationNames = append(locationNames, subs.Names["en"])
|
||||||
|
}
|
||||||
|
location = lo.ToPtr(strings.Join(locationNames, ", "))
|
||||||
|
coordinateX = &record.Location.Latitude
|
||||||
|
coordinateY = &record.Location.Longitude
|
||||||
|
}
|
||||||
|
|
||||||
ticket = models.AuthTicket{
|
ticket = models.AuthTicket{
|
||||||
Claims: []string{"*"},
|
Claims: []string{"*"},
|
||||||
Audiences: []string{InternalTokenAudience},
|
Audiences: []string{InternalTokenAudience},
|
||||||
IpAddress: ip,
|
IpAddress: ip,
|
||||||
UserAgent: ua,
|
UserAgent: ua,
|
||||||
StepRemain: steps,
|
StepRemain: steps,
|
||||||
|
Location: location,
|
||||||
|
CoordinateX: coordinateX,
|
||||||
|
CoordinateY: coordinateY,
|
||||||
ExpiredAt: nil,
|
ExpiredAt: nil,
|
||||||
AvailableAt: nil,
|
AvailableAt: nil,
|
||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}
|
}
|
||||||
|
|
||||||
err := database.C.Save(&ticket).Error
|
err = database.C.Save(&ticket).Error
|
||||||
|
|
||||||
return ticket, err
|
return ticket, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -126,11 +151,15 @@ func ActiveTicket(ticket models.AuthTicket) (models.AuthTicket, error) {
|
|||||||
if err := database.C.Save(&ticket).Error; err != nil {
|
if err := database.C.Save(&ticket).Error; err != nil {
|
||||||
return ticket, err
|
return ticket, err
|
||||||
} else {
|
} else {
|
||||||
|
var account models.Account
|
||||||
|
if err := database.C.Where("id = ?", ticket.AccountID).Select("Language").First(&account).Error; err != nil {
|
||||||
|
return ticket, nil
|
||||||
|
}
|
||||||
|
|
||||||
_ = NewNotification(models.Notification{
|
_ = NewNotification(models.Notification{
|
||||||
Topic: "passport.security.alert",
|
Topic: "passport.security.alert",
|
||||||
Title: "New sign in alert",
|
Title: localize.L.GetLocalizedString("subjectLoginAlert", account.Language),
|
||||||
Subtitle: fmt.Sprintf("New sign in from %s", ticket.IpAddress),
|
Body: fmt.Sprintf(localize.L.GetLocalizedString("shortBodyLoginAlert", account.Language), ticket.IpAddress),
|
||||||
Body: fmt.Sprintf("Your account just got a new sign in from %s. Make sure you recongize this device, or sign out it immediately and reset password.", ticket.IpAddress),
|
|
||||||
Metadata: datatypes.JSONMap{
|
Metadata: datatypes.JSONMap{
|
||||||
"ip_address": ticket.IpAddress,
|
"ip_address": ticket.IpAddress,
|
||||||
"created_at": ticket.CreatedAt,
|
"created_at": ticket.CreatedAt,
|
||||||
@ -214,7 +243,7 @@ func RotateTicket(ticket models.AuthTicket, fullyRestart ...bool) (models.AuthTi
|
|||||||
}
|
}
|
||||||
|
|
||||||
func DoAutoSignoff() {
|
func DoAutoSignoff() {
|
||||||
duration := 7 * 24 * time.Hour
|
duration := viper.GetDuration("security.auto_signoff") * time.Second
|
||||||
deadline := time.Now().Add(-duration)
|
deadline := time.Now().Add(-duration)
|
||||||
|
|
||||||
log.Debug().Time("before", deadline).Msg("Now signing off tickets...")
|
log.Debug().Time("before", deadline).Msg("Now signing off tickets...")
|
||||||
|
@ -2,11 +2,12 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
@ -35,8 +36,7 @@ func GetToken(ticket models.AuthTicket) (atk, rtk string, err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket.LastGrantAt = lo.ToPtr(time.Now())
|
database.C.Model(&ticket).Update("last_grant_at", time.Now())
|
||||||
database.C.Save(&ticket)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -2,65 +2,20 @@ package services
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/localize"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
"git.solsynth.dev/hypernet/pusher/pkg/pushkit"
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"github.com/google/uuid"
|
"github.com/google/uuid"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const ConfirmRegistrationTemplate = `Dear %s,
|
|
||||||
|
|
||||||
Thank you for choosing to register with %s. We are excited to welcome you to our community and appreciate your trust in us.
|
|
||||||
|
|
||||||
Your registration details have been successfully received, and you are now a valued member of %s. Here are the confirm link of your registration:
|
|
||||||
|
|
||||||
%s
|
|
||||||
|
|
||||||
As a confirmed registered member, you will have access to all our services.
|
|
||||||
We encourage you to explore our services and take full advantage of the resources available to you.
|
|
||||||
|
|
||||||
Once again, thank you for choosing us. We look forward to serving you and hope you have a positive experience with us.
|
|
||||||
|
|
||||||
Best regards,
|
|
||||||
%s`
|
|
||||||
|
|
||||||
const ResetPasswordTemplate = `Dear %s,
|
|
||||||
|
|
||||||
We received a request to reset the password for your account at %s. If you did not request a password reset, please ignore this email.
|
|
||||||
|
|
||||||
To confirm your password reset request and create a new password, please use the link below:
|
|
||||||
|
|
||||||
%s
|
|
||||||
|
|
||||||
This link will expire in 24 hours. If you do not reset your password within this time frame, you will need to submit a new password reset request.
|
|
||||||
|
|
||||||
If you have any questions or need further assistance, please do not hesitate to contact our support team.
|
|
||||||
|
|
||||||
Best regards,
|
|
||||||
%s`
|
|
||||||
|
|
||||||
const DeleteAccountTemplate = `Dear %s,
|
|
||||||
|
|
||||||
We received a request to delete your account at %s. If you did not request a account deletion, please change your account password right now.
|
|
||||||
If you changed your mind, please ignore this email.
|
|
||||||
|
|
||||||
To confirm your account deletion request, please use the link below:
|
|
||||||
|
|
||||||
%s
|
|
||||||
|
|
||||||
This link will expire in 24 hours. If you do not use that link within this time frame, you will need to submit an account deletion request.
|
|
||||||
|
|
||||||
If you have any questions or need further assistance, please do not hesitate to contact our support team.
|
|
||||||
Also, if you want to let us know why you decided to delete your account, send email us (lily@solsynth.dev) and tell us how could we improve our user experience.
|
|
||||||
|
|
||||||
Best regards,
|
|
||||||
%s`
|
|
||||||
|
|
||||||
func ValidateMagicToken(code string, mode models.MagicTokenType) (models.MagicToken, error) {
|
func ValidateMagicToken(code string, mode models.MagicTokenType) (models.MagicToken, error) {
|
||||||
var tk models.MagicToken
|
var tk models.MagicToken
|
||||||
if err := database.C.Where(models.MagicToken{Code: code, Type: mode}).First(&tk).Error; err != nil {
|
if err := database.C.Where(models.MagicToken{Code: code, Type: mode}).First(&tk).Error; err != nil {
|
||||||
@ -92,10 +47,15 @@ func NewMagicToken(mode models.MagicTokenType, assignTo *models.Account, expired
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotifyMagicToken(token models.MagicToken) error {
|
func NotifyMagicToken(token models.MagicToken, skipCheck ...bool) error {
|
||||||
if token.AccountID == nil {
|
if token.AccountID == nil {
|
||||||
return fmt.Errorf("could notify a non-assign magic token")
|
return fmt.Errorf("could notify a non-assign magic token")
|
||||||
}
|
}
|
||||||
|
if token.LastNotifiedAt != nil && (len(skipCheck) == 0 || !skipCheck[0]) {
|
||||||
|
if token.LastNotifiedAt.Unix() >= time.Now().Add(-60*time.Minute).Unix() {
|
||||||
|
return fmt.Errorf("magic token has been notified in an hour")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var user models.Account
|
var user models.Account
|
||||||
if err := database.C.Where(&models.Account{
|
if err := database.C.Where(&models.Account{
|
||||||
@ -109,35 +69,25 @@ func NotifyMagicToken(token models.MagicToken) error {
|
|||||||
switch token.Type {
|
switch token.Type {
|
||||||
case models.ConfirmMagicToken:
|
case models.ConfirmMagicToken:
|
||||||
link := fmt.Sprintf("%s/flow/accounts/confirm?code=%s", viper.GetString("frontend_app"), token.Code)
|
link := fmt.Sprintf("%s/flow/accounts/confirm?code=%s", viper.GetString("frontend_app"), token.Code)
|
||||||
subject = fmt.Sprintf("[%s] Confirm your registration", viper.GetString("name"))
|
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectConfirmRegistration", user.Language))
|
||||||
content = fmt.Sprintf(
|
content = localize.L.RenderLocalizedTemplateHTML("register-confirm.tmpl", user.Language, map[string]any{
|
||||||
ConfirmRegistrationTemplate,
|
"User": user,
|
||||||
user.Name,
|
"Link": link,
|
||||||
viper.GetString("name"),
|
})
|
||||||
viper.GetString("maintainer"),
|
|
||||||
link,
|
|
||||||
viper.GetString("maintainer"),
|
|
||||||
)
|
|
||||||
case models.ResetPasswordMagicToken:
|
case models.ResetPasswordMagicToken:
|
||||||
link := fmt.Sprintf("%s/flow/accounts/password-reset?code=%s", viper.GetString("frontend_app"), token.Code)
|
link := fmt.Sprintf("%s/flow/accounts/password-reset?code=%s", viper.GetString("frontend_app"), token.Code)
|
||||||
subject = fmt.Sprintf("[%s] Reset your password", viper.GetString("name"))
|
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectResetPassword", user.Language))
|
||||||
content = fmt.Sprintf(
|
content = localize.L.RenderLocalizedTemplateHTML("reset-password.tmpl", user.Language, map[string]any{
|
||||||
ResetPasswordTemplate,
|
"User": user,
|
||||||
user.Name,
|
"Link": link,
|
||||||
viper.GetString("name"),
|
})
|
||||||
link,
|
|
||||||
viper.GetString("maintainer"),
|
|
||||||
)
|
|
||||||
case models.DeleteAccountMagicToken:
|
case models.DeleteAccountMagicToken:
|
||||||
link := fmt.Sprintf("%s/flow/accounts/account-delete?code=%s", viper.GetString("frontend_app"), token.Code)
|
link := fmt.Sprintf("%s/flow/accounts/deletion?code=%s", viper.GetString("frontend_app"), token.Code)
|
||||||
subject = fmt.Sprintf("[%s] Confirm your account deletion", viper.GetString("name"))
|
subject = fmt.Sprintf("[%s] %s", viper.GetString("name"), localize.L.GetLocalizedString("subjectDeleteAccount", user.Language))
|
||||||
content = fmt.Sprintf(
|
content = localize.L.RenderLocalizedTemplateHTML("confirm-deletion.tmpl", user.Language, map[string]any{
|
||||||
DeleteAccountTemplate,
|
"User": user,
|
||||||
user.Name,
|
"Link": link,
|
||||||
viper.GetString("name"),
|
})
|
||||||
link,
|
|
||||||
viper.GetString("maintainer"),
|
|
||||||
)
|
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported magic token type to notify")
|
return fmt.Errorf("unsupported magic token type to notify")
|
||||||
}
|
}
|
||||||
@ -146,8 +96,13 @@ func NotifyMagicToken(token models.MagicToken) error {
|
|||||||
To: user.GetPrimaryEmail().Content,
|
To: user.GetPrimaryEmail().Content,
|
||||||
Email: pushkit.EmailData{
|
Email: pushkit.EmailData{
|
||||||
Subject: subject,
|
Subject: subject,
|
||||||
Text: &content,
|
HTML: &content,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
database.C.Model(&token).Update("last_notified_at", time.Now())
|
||||||
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -3,7 +3,7 @@ package admin
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
@ -3,7 +3,7 @@ package admin
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
@ -4,7 +4,7 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapAdminAPIs(app *fiber.App, baseURL string) {
|
func MapControllers(app *fiber.App, baseURL string) {
|
||||||
admin := app.Group(baseURL)
|
admin := app.Group(baseURL)
|
||||||
{
|
{
|
||||||
admin.Post("/badges", grantBadge)
|
admin.Post("/badges", grantBadge)
|
@ -3,8 +3,8 @@ package admin
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
)
|
)
|
||||||
@ -75,7 +75,6 @@ func notifyOneUser(c *fiber.Ctx) error {
|
|||||||
Metadata map[string]any `json:"metadata"`
|
Metadata map[string]any `json:"metadata"`
|
||||||
Priority int `json:"priority"`
|
Priority int `json:"priority"`
|
||||||
IsRealtime bool `json:"is_realtime"`
|
IsRealtime bool `json:"is_realtime"`
|
||||||
UserID uint `json:"user_id" validate:"required"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
@ -87,8 +86,10 @@ func notifyOneUser(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
operator := c.Locals("user").(models.Account)
|
operator := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
userId, _ := c.ParamsInt("user", 0)
|
||||||
|
|
||||||
var user models.Account
|
var user models.Account
|
||||||
if err := database.C.Where("id = ?", data.UserID).First(&user).Error; err != nil {
|
if err := database.C.Where("id = ?", userId).First(&user).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddAuditRecord(operator, "notify.one", c.IP(), c.Get(fiber.HeaderUserAgent), map[string]any{
|
services.AddAuditRecord(operator, "notify.one", c.IP(), c.Get(fiber.HeaderUserAgent), map[string]any{
|
@ -5,8 +5,8 @@ import (
|
|||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -34,7 +34,7 @@ func editUserPermission(c *fiber.Ctx) error {
|
|||||||
prev := user.PermNodes
|
prev := user.PermNodes
|
||||||
user.PermNodes = data.PermNodes
|
user.PermNodes = data.PermNodes
|
||||||
|
|
||||||
services.InvalidAuthCacheWithUser(user.ID)
|
services.InvalidUserAuthCache(user.ID)
|
||||||
|
|
||||||
if err := database.C.Save(&user).Error; err != nil {
|
if err := database.C.Save(&user).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
@ -4,8 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -2,21 +2,64 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"gorm.io/gorm"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
jsoniter "github.com/json-iterator/go"
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
"github.com/samber/lo"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func getUserInBatch(c *fiber.Ctx) error {
|
||||||
|
id := c.Query("id")
|
||||||
|
list := strings.Split(id, ",")
|
||||||
|
var nameList []string
|
||||||
|
numericList := lo.Filter(lo.Map(list, func(str string, i int) int {
|
||||||
|
value, err := strconv.Atoi(str)
|
||||||
|
if err != nil {
|
||||||
|
nameList = append(nameList, str)
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return value
|
||||||
|
}), func(vak int, idx int) bool {
|
||||||
|
return vak > 0
|
||||||
|
})
|
||||||
|
|
||||||
|
tx := database.C
|
||||||
|
if len(numericList) > 0 {
|
||||||
|
tx = tx.Where("id IN ?", numericList)
|
||||||
|
}
|
||||||
|
if len(nameList) > 0 {
|
||||||
|
tx = tx.Or("name IN ?", nameList)
|
||||||
|
}
|
||||||
|
if len(nameList) == 0 && len(numericList) == 0 {
|
||||||
|
return c.JSON([]models.Account{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var accounts []models.Account
|
||||||
|
if err := tx.
|
||||||
|
Preload("Profile").
|
||||||
|
Preload("Badges", func(db *gorm.DB) *gorm.DB {
|
||||||
|
return db.Order("badges.is_active DESC, badges.type DESC")
|
||||||
|
}).
|
||||||
|
Find(&accounts).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(accounts)
|
||||||
|
}
|
||||||
|
|
||||||
func lookupAccount(c *fiber.Ctx) error {
|
func lookupAccount(c *fiber.Ctx) error {
|
||||||
probe := c.Query("probe")
|
probe := c.Query("probe")
|
||||||
if len(probe) == 0 {
|
if len(probe) == 0 {
|
||||||
@ -65,54 +108,11 @@ func getUserinfo(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
var resp fiber.Map
|
var resp fiber.Map
|
||||||
raw, _ := jsoniter.Marshal(data)
|
raw, _ := jsoniter.Marshal(data)
|
||||||
jsoniter.Unmarshal(raw, &resp)
|
_ = jsoniter.Unmarshal(raw, &resp)
|
||||||
|
|
||||||
resp["sub"] = strconv.Itoa(int(data.ID))
|
|
||||||
resp["family_name"] = data.Profile.FirstName
|
|
||||||
resp["given_name"] = data.Profile.LastName
|
|
||||||
resp["name"] = data.Name
|
|
||||||
resp["email"] = data.GetPrimaryEmail().Content
|
|
||||||
resp["preferred_username"] = data.Nick
|
|
||||||
|
|
||||||
if data.Avatar != nil {
|
|
||||||
resp["picture"] = *data.GetAvatar()
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(resp)
|
return c.JSON(resp)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getEvents(c *fiber.Ctx) error {
|
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
user := c.Locals("user").(models.Account)
|
|
||||||
take := c.QueryInt("take", 0)
|
|
||||||
offset := c.QueryInt("offset", 0)
|
|
||||||
|
|
||||||
var count int64
|
|
||||||
var events []models.ActionEvent
|
|
||||||
if err := database.C.
|
|
||||||
Where(&models.ActionEvent{AccountID: user.ID}).
|
|
||||||
Model(&models.ActionEvent{}).
|
|
||||||
Count(&count).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := database.C.
|
|
||||||
Order("created_at desc").
|
|
||||||
Where(&models.ActionEvent{AccountID: user.ID}).
|
|
||||||
Limit(take).
|
|
||||||
Offset(offset).
|
|
||||||
Find(&events).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
|
||||||
"count": count,
|
|
||||||
"data": events,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func editUserinfo(c *fiber.Ctx) error {
|
func editUserinfo(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -124,6 +124,11 @@ func editUserinfo(c *fiber.Ctx) error {
|
|||||||
Description string `json:"description"`
|
Description string `json:"description"`
|
||||||
FirstName string `json:"first_name"`
|
FirstName string `json:"first_name"`
|
||||||
LastName string `json:"last_name"`
|
LastName string `json:"last_name"`
|
||||||
|
Location string `json:"location"`
|
||||||
|
TimeZone string `json:"time_zone"`
|
||||||
|
Gender string `json:"gender"`
|
||||||
|
Pronouns string `json:"pronouns"`
|
||||||
|
Links map[string]string `json:"links"`
|
||||||
Birthday time.Time `json:"birthday"`
|
Birthday time.Time `json:"birthday"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +137,7 @@ func editUserinfo(c *fiber.Ctx) error {
|
|||||||
} else {
|
} else {
|
||||||
data.Nick = strings.TrimSpace(data.Nick)
|
data.Nick = strings.TrimSpace(data.Nick)
|
||||||
}
|
}
|
||||||
if !services.ValidateAccountName(data.Nick, 4, 24) {
|
if !services.ValidateAccountName(data.Nick, 1, 24) {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "invalid account nick, length requires 4 to 24")
|
return fiber.NewError(fiber.StatusBadRequest, "invalid account nick, length requires 4 to 24")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -144,8 +149,18 @@ func editUserinfo(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
links := make(map[string]any)
|
||||||
|
for k, v := range data.Links {
|
||||||
|
links[k] = v
|
||||||
|
}
|
||||||
|
|
||||||
account.Nick = data.Nick
|
account.Nick = data.Nick
|
||||||
account.Description = data.Description
|
account.Profile.Gender = data.Gender
|
||||||
|
account.Profile.Pronouns = data.Pronouns
|
||||||
|
account.Profile.Location = data.Location
|
||||||
|
account.Profile.TimeZone = data.TimeZone
|
||||||
|
account.Profile.Links = links
|
||||||
|
account.Profile.Description = data.Description
|
||||||
account.Profile.FirstName = data.FirstName
|
account.Profile.FirstName = data.FirstName
|
||||||
account.Profile.LastName = data.LastName
|
account.Profile.LastName = data.LastName
|
||||||
account.Profile.Birthday = &data.Birthday
|
account.Profile.Birthday = &data.Birthday
|
||||||
@ -156,18 +171,47 @@ func editUserinfo(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddEvent(user.ID, "profile.edit", strconv.Itoa(int(user.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "profile.edit", nil, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
services.InvalidAuthCacheWithUser(account.ID)
|
services.InvalidUserAuthCache(account.ID)
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateAccountLanguage(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Language string `json:"language" validate:"required,bcp47_language_tag"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Model(&models.Account{}).Where("id = ?", user.ID).
|
||||||
|
Updates(&models.Account{Language: data.Language}).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
services.AddEvent(user.ID, "profile.edit.language", nil, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
|
services.InvalidUserAuthCache(user.ID)
|
||||||
|
|
||||||
|
user.Language = data.Language
|
||||||
|
|
||||||
|
return c.JSON(user)
|
||||||
|
}
|
||||||
|
|
||||||
func doRegister(c *fiber.Ctx) error {
|
func doRegister(c *fiber.Ctx) error {
|
||||||
var data struct {
|
var data struct {
|
||||||
Name string `json:"name" validate:"required,lowercase,alphanum,min=4,max=16"`
|
Name string `json:"name" validate:"required,lowercase,alphanum,min=4,max=16"`
|
||||||
Nick string `json:"nick" validate:"required"`
|
Nick string `json:"nick" validate:"required"`
|
||||||
Email string `json:"email" validate:"required,email"`
|
Email string `json:"email" validate:"required,email"`
|
||||||
Password string `json:"password" validate:"required,min=4,max=32"`
|
Password string `json:"password" validate:"required,min=4,max=32"`
|
||||||
|
Language string `json:"language" validate:"required,bcp47_language_tag"`
|
||||||
|
CaptchaToken string `json:"captcha_token" validate:"required"`
|
||||||
MagicToken string `json:"magic_token"`
|
MagicToken string `json:"magic_token"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +222,10 @@ func doRegister(c *fiber.Ctx) error {
|
|||||||
data.Nick = strings.TrimSpace(data.Nick)
|
data.Nick = strings.TrimSpace(data.Nick)
|
||||||
data.Email = strings.TrimSpace(data.Email)
|
data.Email = strings.TrimSpace(data.Email)
|
||||||
}
|
}
|
||||||
if !services.ValidateAccountName(data.Nick, 4, 24) {
|
if _, err := strconv.Atoi(data.Name); err == nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "invalid account name, cannot be pure number")
|
||||||
|
}
|
||||||
|
if !services.ValidateAccountName(data.Nick, 1, 24) {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "invalid account nick, length requires 4 to 24")
|
return fiber.NewError(fiber.StatusBadRequest, "invalid account nick, length requires 4 to 24")
|
||||||
}
|
}
|
||||||
if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 {
|
if viper.GetBool("use_registration_magic_token") && len(data.MagicToken) <= 0 {
|
||||||
@ -191,11 +238,16 @@ func doRegister(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if !gap.Nx.ValidateCaptcha(data.CaptchaToken, c.IP()) {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "captcha check failed")
|
||||||
|
}
|
||||||
|
|
||||||
if user, err := services.CreateAccount(
|
if user, err := services.CreateAccount(
|
||||||
data.Name,
|
data.Name,
|
||||||
data.Nick,
|
data.Nick,
|
||||||
data.Email,
|
data.Email,
|
||||||
data.Password,
|
data.Password,
|
||||||
|
data.Language,
|
||||||
); err != nil {
|
); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
@ -219,6 +271,24 @@ func doRegisterConfirm(c *fiber.Ctx) error {
|
|||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func reNotifyRegisterConfirm(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var magicToken models.MagicToken
|
||||||
|
if err := database.C.Where("account_id = ? AND type = ?", user.ID, models.ConfirmMagicToken).First(&magicToken).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.NotifyMagicToken(magicToken); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
func requestDeleteAccount(c *fiber.Ctx) error {
|
func requestDeleteAccount(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
@ -4,7 +4,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
|
||||||
@ -37,10 +37,10 @@ func doAuthenticate(c *fiber.Ctx) error {
|
|||||||
user, err := services.LookupAccount(data.Username)
|
user, err := services.LookupAccount(data.Username)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("account was not found: %v", err.Error()))
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("account was not found: %v", err.Error()))
|
||||||
} else if user.ConfirmedAt == nil {
|
|
||||||
return fiber.NewError(fiber.StatusForbidden, "account was not confirmed; check your inbox, there will be an email lead you confirm your registration")
|
|
||||||
} else if user.SuspendedAt != nil {
|
} else if user.SuspendedAt != nil {
|
||||||
return fiber.NewError(fiber.StatusForbidden, "account was suspended")
|
return fiber.NewError(fiber.StatusForbidden, "account was suspended")
|
||||||
|
} else if err := services.CheckLoginAbility(user); err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket, err := services.NewTicket(user, c.IP(), c.Get(fiber.HeaderUserAgent))
|
ticket, err := services.NewTicket(user, c.IP(), c.Get(fiber.HeaderUserAgent))
|
@ -1,12 +1,14 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/paperclip/pkg/filekit"
|
||||||
|
"git.solsynth.dev/hypernet/paperclip/pkg/proto"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func setAvatar(c *fiber.Ctx) error {
|
func setAvatar(c *fiber.Ctx) error {
|
||||||
@ -23,15 +25,25 @@ func setAvatar(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Avatar = &data.AttachmentID
|
og := user.Avatar
|
||||||
|
if err := database.C.Model(&user).Update("avatar", data.AttachmentID).Error; err != nil {
|
||||||
if err := database.C.Save(&user).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "profile.edit.avatar", strconv.Itoa(int(user.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "profile.edit.avatar", nil, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
services.InvalidAuthCacheWithUser(user.ID)
|
services.InvalidUserAuthCache(user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if og != nil && len(*og) > 0 {
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &proto.UpdateUsageRequest{
|
||||||
|
Rid: []string{*og},
|
||||||
|
Delta: -1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &proto.UpdateUsageRequest{
|
||||||
|
Rid: []string{*user.Avatar},
|
||||||
|
Delta: 1,
|
||||||
|
})
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,15 +61,25 @@ func setBanner(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Banner = &data.AttachmentID
|
og := user.Banner
|
||||||
|
if err := database.C.Model(&user).Update("banner", data.AttachmentID).Error; err != nil {
|
||||||
if err := database.C.Save(&user).Error; err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "profile.edit.banner", strconv.Itoa(int(user.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "profile.edit.banner", nil, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
services.InvalidAuthCacheWithUser(user.ID)
|
services.InvalidUserAuthCache(user.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if og != nil && len(*og) > 0 {
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &proto.UpdateUsageRequest{
|
||||||
|
Rid: []string{*og},
|
||||||
|
Delta: -1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
filekit.CountAttachmentUsage(gap.Nx, &proto.UpdateUsageRequest{
|
||||||
|
Rid: []string{*user.Banner},
|
||||||
|
Delta: 1,
|
||||||
|
})
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
|
|
42
pkg/internal/web/api/badges_api.go
Normal file
42
pkg/internal/web/api/badges_api.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listUserBadge(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var badges []models.Badge
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).Find(&badges).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(badges)
|
||||||
|
}
|
||||||
|
|
||||||
|
func activeUserBadge(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
badgeId, _ := c.ParamsInt("badgeId", 0)
|
||||||
|
|
||||||
|
var badge models.Badge
|
||||||
|
if err := database.C.Where("id = ? AND account_id = ?", badgeId, user.ID).First(&badge).Error; err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.ActiveBadge(badge); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
@ -4,8 +4,8 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
@ -3,8 +3,8 @@ package api
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"gorm.io/datatypes"
|
"gorm.io/datatypes"
|
@ -3,13 +3,13 @@ package api
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func listDailySignRecord(c *fiber.Ctx) error {
|
func listCheckInRecord(c *fiber.Ctx) error {
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
@ -20,13 +20,13 @@ func listDailySignRecord(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Model(&models.SignRecord{}).
|
Model(&models.CheckInRecord{}).
|
||||||
Where("account_id = ?", user.ID).
|
Where("account_id = ?", user.ID).
|
||||||
Count(&count).Error; err != nil {
|
Count(&count).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var records []models.SignRecord
|
var records []models.CheckInRecord
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where("account_id = ?", user.ID).
|
Where("account_id = ?", user.ID).
|
||||||
Limit(take).Offset(offset).
|
Limit(take).Offset(offset).
|
||||||
@ -41,7 +41,7 @@ func listDailySignRecord(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func listOtherUserDailySignRecord(c *fiber.Ctx) error {
|
func listOtherUserCheckInRecord(c *fiber.Ctx) error {
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
@ -56,13 +56,13 @@ func listOtherUserDailySignRecord(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
var count int64
|
var count int64
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Model(&models.SignRecord{}).
|
Model(&models.CheckInRecord{}).
|
||||||
Where("account_id = ?", account.ID).
|
Where("account_id = ?", account.ID).
|
||||||
Count(&count).Error; err != nil {
|
Count(&count).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var records []models.SignRecord
|
var records []models.CheckInRecord
|
||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where("account_id = ?", account.ID).
|
Where("account_id = ?", account.ID).
|
||||||
Limit(take).Offset(offset).
|
Limit(take).Offset(offset).
|
||||||
@ -77,29 +77,42 @@ func listOtherUserDailySignRecord(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func getTodayDailySign(c *fiber.Ctx) error {
|
func getTodayCheckIn(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
if record, err := services.GetTodayDailySign(user); err != nil {
|
if record, err := services.GetTodayCheckIn(user); err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.JSON(record)
|
return c.JSON(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func doDailySign(c *fiber.Ctx) error {
|
func doCheckIn(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
if record, err := services.DailySign(user); err != nil {
|
var data struct {
|
||||||
|
CaptchaToken string `json:"captcha_token" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !gap.Nx.ValidateCaptcha(data.CaptchaToken, c.IP()) {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "captcha check failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
if record, err := services.CheckIn(user); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "dailySign", strconv.Itoa(int(record.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "checkIn", map[string]any{
|
||||||
|
"check_in_record": record,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(record)
|
return c.JSON(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
130
pkg/internal/web/api/contacts_api.go
Normal file
130
pkg/internal/web/api/contacts_api.go
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listContact(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var contacts []models.AccountContact
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).Find(&contacts).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(contacts)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getContact(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
contactId, _ := c.ParamsInt("contactId")
|
||||||
|
|
||||||
|
var contact models.AccountContact
|
||||||
|
if err := database.C.Where("account_id = ? AND id = ?", user.ID, contactId).First(&contact).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContact(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Type int8 `json:"type"`
|
||||||
|
Content string `json:"content" validate:"required"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
contact := models.AccountContact{
|
||||||
|
Type: data.Type,
|
||||||
|
Content: data.Content,
|
||||||
|
IsPublic: data.IsPublic,
|
||||||
|
IsPrimary: false,
|
||||||
|
VerifiedAt: nil,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}
|
||||||
|
if err := database.C.Create(&contact).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateContact(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
contactId, _ := c.ParamsInt("contactId")
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Type int8 `json:"type"`
|
||||||
|
Content string `json:"content" validate:"required"`
|
||||||
|
IsPublic bool `json:"is_public"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var contact models.AccountContact
|
||||||
|
if err := database.C.Where("account_id = ? AND id = ?", user.ID, contactId).First(&contact).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
contact.Type = data.Type
|
||||||
|
contact.IsPublic = data.IsPublic
|
||||||
|
if contact.Content != data.Content {
|
||||||
|
contact.Content = data.Content
|
||||||
|
contact.VerifiedAt = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Save(&contact).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(contact)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteContact(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
contactId, _ := c.ParamsInt("contactId")
|
||||||
|
|
||||||
|
var contact models.AccountContact
|
||||||
|
if err := database.C.Where("account_id = ? AND id = ?", user.ID, contactId).First(&contact).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
if contact.IsPrimary {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "cannot delete primary contact")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Delete(&contact).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
40
pkg/internal/web/api/events_api.go
Normal file
40
pkg/internal/web/api/events_api.go
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getEvents(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
take := c.QueryInt("take", 0)
|
||||||
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
var events []models.ActionEvent
|
||||||
|
if err := database.C.
|
||||||
|
Where(&models.ActionEvent{AccountID: user.ID}).
|
||||||
|
Model(&models.ActionEvent{}).
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.
|
||||||
|
Order("created_at desc").
|
||||||
|
Where(&models.ActionEvent{AccountID: user.ID}).
|
||||||
|
Limit(take).
|
||||||
|
Offset(offset).
|
||||||
|
Find(&events).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
"data": events,
|
||||||
|
})
|
||||||
|
}
|
167
pkg/internal/web/api/factors_api.go
Normal file
167
pkg/internal/web/api/factors_api.go
Normal file
@ -0,0 +1,167 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/pquerna/otp/totp"
|
||||||
|
"github.com/samber/lo"
|
||||||
|
"github.com/spf13/viper"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAvailableFactors(c *fiber.Ctx) error {
|
||||||
|
ticketId := c.QueryInt("ticketId", 0)
|
||||||
|
if ticketId <= 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "must provide ticket id as a query parameter")
|
||||||
|
}
|
||||||
|
|
||||||
|
ticket, err := services.GetTicket(uint(ticketId))
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("ticket was not found: %v", err))
|
||||||
|
}
|
||||||
|
factors, err := services.ListUserFactor(ticket.AccountID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(factors)
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestFactorToken(c *fiber.Ctx) error {
|
||||||
|
id, _ := c.ParamsInt("factorId", 0)
|
||||||
|
|
||||||
|
factor, err := services.GetFactor(uint(id))
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if sent, err := services.GetFactorCode(factor, c.IP()); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else if !sent {
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
} else {
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func listFactor(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var factors []models.AuthFactor
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).Find(&factors).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(factors)
|
||||||
|
}
|
||||||
|
|
||||||
|
func createFactor(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Type models.AuthFactorType `json:"type"`
|
||||||
|
Secret string `json:"secret"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
typeWhitelist := []models.AuthFactorType{
|
||||||
|
models.EmailPasswordFactor,
|
||||||
|
models.InAppNotifyFactor,
|
||||||
|
models.TimeOtpFactor,
|
||||||
|
}
|
||||||
|
if !lo.Contains(typeWhitelist, data.Type) {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "invalid factor type")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Currently, each type of factor can only be created once
|
||||||
|
var currentCount int64
|
||||||
|
if err := database.C.Model(&models.AuthFactor{}).
|
||||||
|
Where("account_id = ? AND type = ?", user.ID, data.Type).
|
||||||
|
Count(¤tCount).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, fmt.Sprintf("unable to check current factor count: %v", err))
|
||||||
|
} else if currentCount > 0 {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "this type of factor already exists")
|
||||||
|
}
|
||||||
|
|
||||||
|
factor := models.AuthFactor{
|
||||||
|
Type: data.Type,
|
||||||
|
Secret: data.Secret,
|
||||||
|
Account: user,
|
||||||
|
AccountID: user.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
additionalOnceConfig := map[string]any{}
|
||||||
|
|
||||||
|
switch data.Type {
|
||||||
|
case models.TimeOtpFactor:
|
||||||
|
cfg := totp.GenerateOpts{
|
||||||
|
Issuer: viper.GetString("name"),
|
||||||
|
AccountName: user.Name,
|
||||||
|
Period: 30,
|
||||||
|
SecretSize: 20,
|
||||||
|
Digits: 6,
|
||||||
|
}
|
||||||
|
key, err := totp.Generate(cfg)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("unable to generate totp key: %v", err)
|
||||||
|
}
|
||||||
|
factor.Secret = key.Secret()
|
||||||
|
factor.Config = map[string]any{
|
||||||
|
"issuer": cfg.Issuer,
|
||||||
|
"account_name": cfg.AccountName,
|
||||||
|
"period": cfg.Period,
|
||||||
|
"secret_size": cfg.SecretSize,
|
||||||
|
"digits": cfg.Digits,
|
||||||
|
}
|
||||||
|
additionalOnceConfig["url"] = key.URL()
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Create(&factor).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(additionalOnceConfig) > 0 {
|
||||||
|
for k, v := range additionalOnceConfig {
|
||||||
|
factor.Config[k] = v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(factor)
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteFactor(c *fiber.Ctx) error {
|
||||||
|
id, _ := c.ParamsInt("factorId", 0)
|
||||||
|
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var factor models.AuthFactor
|
||||||
|
if err := database.C.Where("id = ? AND account_id = ?", id, user.ID).First(&factor).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if factor.Type == models.PasswordAuthFactor {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, "unable to delete password factor")
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := database.C.Delete(&factor).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
@ -4,26 +4,28 @@ import (
|
|||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MapAPIs(app *fiber.App, baseURL string) {
|
func MapControllers(app *fiber.App, baseURL string) {
|
||||||
app.Get("/.well-known/openid-configuration", getOidcConfiguration)
|
|
||||||
app.Get("/.well-known/jwks", getJwk)
|
|
||||||
|
|
||||||
api := app.Group(baseURL).Name("API")
|
api := app.Group(baseURL).Name("API")
|
||||||
{
|
{
|
||||||
daily := api.Group("/daily").Name("Daily Sign API")
|
api.Get("/well-known/openid-configuration", getOidcConfiguration)
|
||||||
|
api.Get("/well-known/jwks", getJwk)
|
||||||
|
|
||||||
|
checkIn := api.Group("/check-in").Name("Daily Check In API")
|
||||||
{
|
{
|
||||||
daily.Get("/", listDailySignRecord)
|
checkIn.Get("/", listCheckInRecord)
|
||||||
daily.Get("/today", getTodayDailySign)
|
checkIn.Get("/today", getTodayCheckIn)
|
||||||
daily.Post("/", doDailySign)
|
checkIn.Post("/", doCheckIn)
|
||||||
}
|
}
|
||||||
|
|
||||||
notify := api.Group("/notifications").Name("Notifications API")
|
notify := api.Group("/notifications").Name("Notifications API")
|
||||||
{
|
{
|
||||||
notify.Get("/", getNotifications)
|
notify.Get("/", listNotification)
|
||||||
|
notify.Get("/count", getNotificationCount)
|
||||||
notify.Get("/subscription", getNotifySubscriber)
|
notify.Get("/subscription", getNotifySubscriber)
|
||||||
notify.Post("/subscription", addNotifySubscriber)
|
notify.Post("/subscription", addNotifySubscriber)
|
||||||
notify.Delete("/subscription/:deviceId", removeNotifySubscriber)
|
notify.Delete("/subscription/:deviceId", removeNotifySubscriber)
|
||||||
notify.Put("/read", markNotificationReadBatch)
|
notify.Put("/read", markNotificationReadBatch)
|
||||||
|
notify.Put("/read/all", markNotificationAllRead)
|
||||||
notify.Put("/read/:notificationId", markNotificationRead)
|
notify.Put("/read/:notificationId", markNotificationRead)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,6 +37,12 @@ func MapAPIs(app *fiber.App, baseURL string) {
|
|||||||
preferences.Put("/notifications", updateNotificationPreference)
|
preferences.Put("/notifications", updateNotificationPreference)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
badges := api.Group("/badges").Name("Badges")
|
||||||
|
{
|
||||||
|
badges.Get("/me", listUserBadge)
|
||||||
|
badges.Post("/:badgeId/active", activeUserBadge)
|
||||||
|
}
|
||||||
|
|
||||||
reports := api.Group("/reports").Name("Reports API")
|
reports := api.Group("/reports").Name("Reports API")
|
||||||
{
|
{
|
||||||
abuse := reports.Group("/abuse").Name("Abuse Reports")
|
abuse := reports.Group("/abuse").Name("Abuse Reports")
|
||||||
@ -46,33 +54,66 @@ func MapAPIs(app *fiber.App, baseURL string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
punishments := api.Group("/punishments").Name("Punishments API")
|
||||||
|
{
|
||||||
|
punishments.Get("/", listUserPunishment)
|
||||||
|
punishments.Get("/given", listMadePunishment)
|
||||||
|
punishments.Get("/:id", getPunishment)
|
||||||
|
punishments.Post("/", createPunishment)
|
||||||
|
punishments.Put("/:id", editPunishment)
|
||||||
|
punishments.Delete("/:id", deletePunishment)
|
||||||
|
}
|
||||||
|
|
||||||
|
api.Get("/users", getUserInBatch)
|
||||||
api.Get("/users/lookup", lookupAccount)
|
api.Get("/users/lookup", lookupAccount)
|
||||||
api.Get("/users/search", searchAccount)
|
api.Get("/users/search", searchAccount)
|
||||||
|
|
||||||
me := api.Group("/users/me").Name("Myself Operations")
|
me := api.Group("/users/me").Name("Myself Operations")
|
||||||
{
|
{
|
||||||
|
|
||||||
me.Get("/avatar", getAvatar)
|
me.Get("/avatar", getAvatar)
|
||||||
me.Get("/banner", getBanner)
|
me.Get("/banner", getBanner)
|
||||||
me.Put("/avatar", setAvatar)
|
me.Put("/avatar", setAvatar)
|
||||||
me.Put("/banner", setBanner)
|
me.Put("/banner", setBanner)
|
||||||
|
|
||||||
me.Get("/", getUserinfo)
|
me.Get("/", getUserinfo)
|
||||||
|
me.Get("/oidc", getUserinfoForOidc)
|
||||||
me.Put("/", editUserinfo)
|
me.Put("/", editUserinfo)
|
||||||
|
me.Put("/language", updateAccountLanguage)
|
||||||
me.Get("/events", getEvents)
|
me.Get("/events", getEvents)
|
||||||
me.Get("/tickets", getTickets)
|
me.Get("/tickets", getTickets)
|
||||||
me.Delete("/tickets/:ticketId", killTicket)
|
me.Delete("/tickets/:ticketId", deleteTicket)
|
||||||
|
|
||||||
me.Post("/confirm", doRegisterConfirm)
|
me.Post("/confirm", doRegisterConfirm)
|
||||||
|
me.Patch("/confirm", reNotifyRegisterConfirm)
|
||||||
|
|
||||||
me.Get("/status", getMyselfStatus)
|
me.Get("/status", getMyselfStatus)
|
||||||
me.Post("/status", setStatus)
|
me.Post("/status", setStatus)
|
||||||
me.Put("/status", editStatus)
|
me.Put("/status", editStatus)
|
||||||
me.Delete("/status", clearStatus)
|
me.Delete("/status", clearStatus)
|
||||||
|
|
||||||
|
me.Get("/pages", getOwnAccountPage)
|
||||||
|
me.Put("/pages", updateAccountPage)
|
||||||
|
|
||||||
|
contacts := me.Group("/contacts").Name("Contacts")
|
||||||
|
{
|
||||||
|
contacts.Get("/", listContact)
|
||||||
|
contacts.Get("/:contactId", getContact)
|
||||||
|
contacts.Post("/", createContact)
|
||||||
|
contacts.Put("/:contactId", updateContact)
|
||||||
|
contacts.Delete("/:contactId", deleteContact)
|
||||||
|
}
|
||||||
|
|
||||||
|
factors := me.Group("/factors").Name("Factors")
|
||||||
|
{
|
||||||
|
factors.Get("/", listFactor)
|
||||||
|
factors.Post("/", createFactor)
|
||||||
|
factors.Delete("/:factorId", deleteFactor)
|
||||||
|
}
|
||||||
|
|
||||||
relations := me.Group("/relations").Name("Relations")
|
relations := me.Group("/relations").Name("Relations")
|
||||||
{
|
{
|
||||||
relations.Post("/", makeFriendship)
|
relations.Post("/", makeFriendship)
|
||||||
|
relations.Post("/friend", makeFriendship)
|
||||||
relations.Post("/block", makeBlockship)
|
relations.Post("/block", makeBlockship)
|
||||||
|
|
||||||
relations.Get("/", listRelationship)
|
relations.Get("/", listRelationship)
|
||||||
@ -96,8 +137,9 @@ func MapAPIs(app *fiber.App, baseURL string) {
|
|||||||
{
|
{
|
||||||
directory.Get("/", getOtherUserinfo)
|
directory.Get("/", getOtherUserinfo)
|
||||||
directory.Get("/status", getStatus)
|
directory.Get("/status", getStatus)
|
||||||
|
directory.Get("/page", getAccountPage)
|
||||||
|
|
||||||
directory.Get("/daily", listOtherUserDailySignRecord)
|
directory.Get("/check-in", listOtherUserCheckInRecord)
|
||||||
}
|
}
|
||||||
|
|
||||||
api.Get("/users", getOtherUserinfoBatch)
|
api.Get("/users", getOtherUserinfoBatch)
|
||||||
@ -130,13 +172,23 @@ func MapAPIs(app *fiber.App, baseURL string) {
|
|||||||
realms.Put("/:realmId", editRealm)
|
realms.Put("/:realmId", editRealm)
|
||||||
realms.Delete("/:realmId", deleteRealm)
|
realms.Delete("/:realmId", deleteRealm)
|
||||||
realms.Post("/:realm/members", addRealmMember)
|
realms.Post("/:realm/members", addRealmMember)
|
||||||
realms.Delete("/:realm/members", removeRealmMember)
|
realms.Delete("/:realm/members/:memberId", removeRealmMember)
|
||||||
realms.Delete("/:realm/members/me", leaveRealm)
|
realms.Delete("/:realm/me", leaveRealm)
|
||||||
|
}
|
||||||
|
|
||||||
|
programs := api.Group("/programs").Name("Programs API")
|
||||||
|
{
|
||||||
|
programs.Get("/", listProgram)
|
||||||
|
programs.Get("/members", listProgramMembership)
|
||||||
|
programs.Get("/:programId", getProgram)
|
||||||
|
programs.Post("/:programId", joinProgram)
|
||||||
|
programs.Delete("/:programId", leaveProgram)
|
||||||
}
|
}
|
||||||
|
|
||||||
developers := api.Group("/dev").Name("Developers API")
|
developers := api.Group("/dev").Name("Developers API")
|
||||||
{
|
{
|
||||||
developers.Post("/notify", notifyUser)
|
developers.Post("/notify/:user", notifyUser)
|
||||||
|
developers.Post("/notify/all", notifyAllUser)
|
||||||
|
|
||||||
bots := developers.Group("/bots").Name("Bots")
|
bots := developers.Group("/bots").Name("Bots")
|
||||||
{
|
{
|
||||||
@ -165,6 +217,9 @@ func MapAPIs(app *fiber.App, baseURL string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
api.Post("/permissions/check", checkPermission)
|
||||||
|
api.Post("/permissions/check/:userId", checkUserPermission)
|
||||||
|
|
||||||
api.All("/*", func(c *fiber.Ctx) error {
|
api.All("/*", func(c *fiber.Ctx) error {
|
||||||
return fiber.ErrNotFound
|
return fiber.ErrNotFound
|
||||||
})
|
})
|
@ -1,17 +1,17 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
"strconv"
|
|
||||||
"time"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getNotifications(c *fiber.Ctx) error {
|
func listNotification(c *fiber.Ctx) error {
|
||||||
take := c.QueryInt("take", 0)
|
take := c.QueryInt("take", 0)
|
||||||
offset := c.QueryInt("offset", 0)
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ func getNotifications(c *fiber.Ctx) error {
|
|||||||
if err := tx.
|
if err := tx.
|
||||||
Limit(take).
|
Limit(take).
|
||||||
Offset(offset).
|
Offset(offset).
|
||||||
Order("read_at DESC, created_at DESC").
|
Order("created_at DESC").
|
||||||
Find(¬ifications).Error; err != nil {
|
Find(¬ifications).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
@ -43,6 +43,25 @@ func getNotifications(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getNotificationCount(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
tx := database.C.Where("account_id = ? AND read_at IS NULL", user.ID).Model(&models.Notification{})
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if err := tx.
|
||||||
|
Count(&count).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func markNotificationRead(c *fiber.Ctx) error {
|
func markNotificationRead(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -67,7 +86,9 @@ func markNotificationRead(c *fiber.Ctx) error {
|
|||||||
if err := database.C.Save(¬ify).Error; err != nil {
|
if err := database.C.Save(¬ify).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "notifications.mark.read", strconv.Itoa(int(notify.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "notifications.mark.read", map[string]any{
|
||||||
|
"notification_id": notify.ID,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -91,11 +112,33 @@ func markNotificationReadBatch(c *fiber.Ctx) error {
|
|||||||
Updates(&models.Notification{ReadAt: lo.ToPtr(time.Now())}).Error; err != nil {
|
Updates(&models.Notification{ReadAt: lo.ToPtr(time.Now())}).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "notifications.markAll.read", strconv.Itoa(int(user.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "notifications.markBatch.read", map[string]any{
|
||||||
|
"notification_id": data.MessageIDs,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func markNotificationAllRead(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
if tx := database.C.Model(&models.Notification{}).
|
||||||
|
Where("account_id = ? AND read_at IS NULL", user.ID).
|
||||||
|
Updates(&models.Notification{ReadAt: lo.ToPtr(time.Now())}); tx.Error != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, tx.Error.Error())
|
||||||
|
} else {
|
||||||
|
services.AddEvent(user.ID, "notifications.markAll.read", map[string]any{
|
||||||
|
"count": tx.RowsAffected,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": tx.RowsAffected,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func getNotifySubscriber(c *fiber.Ctx) error {
|
func getNotifySubscriber(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -148,7 +191,9 @@ func addNotifySubscriber(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddEvent(user.ID, "notifications.subscribe.push", data.DeviceID, c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "notifications.subscribe.push", map[string]any{
|
||||||
|
"device_id": data.DeviceID,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(subscriber)
|
return c.JSON(subscriber)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -167,6 +212,8 @@ func removeNotifySubscriber(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
services.AddEvent(user.ID, "notifications.unsubscribe.push", device, c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "notifications.unsubscribe.push", map[string]any{
|
||||||
|
"device_id": device,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
125
pkg/internal/web/api/notify_api.go
Normal file
125
pkg/internal/web/api/notify_api.go
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func notifyUser(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureGrantedPerm(c, "DevNotifyUser", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
ClientID string `json:"client_id" validate:"required"`
|
||||||
|
Topic string `json:"type" validate:"required"`
|
||||||
|
Title string `json:"subject" validate:"required,max=1024"`
|
||||||
|
Subtitle string `json:"subtitle" validate:"max=1024"`
|
||||||
|
Body string `json:"content" validate:"required,max=4096"`
|
||||||
|
Metadata map[string]any `json:"metadata"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
IsRealtime bool `json:"is_realtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := services.GetThirdClientWithUser(data.ClientID, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to get client: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
userId, _ := c.ParamsInt("user")
|
||||||
|
|
||||||
|
var target models.Account
|
||||||
|
if target, err = services.GetAccount(uint(userId)); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
notification := models.Notification{
|
||||||
|
Topic: data.Topic,
|
||||||
|
Subtitle: data.Subtitle,
|
||||||
|
Title: data.Title,
|
||||||
|
Body: data.Body,
|
||||||
|
Metadata: data.Metadata,
|
||||||
|
Priority: data.Priority,
|
||||||
|
Account: target,
|
||||||
|
AccountID: target.ID,
|
||||||
|
SenderID: &client.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.IsRealtime {
|
||||||
|
if err := services.PushNotification(notification); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := services.NewNotification(notification); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func notifyAllUser(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureGrantedPerm(c, "DevNotifyAllUser", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
ClientID string `json:"client_id" validate:"required"`
|
||||||
|
Topic string `json:"type" validate:"required"`
|
||||||
|
Title string `json:"subject" validate:"required,max=1024"`
|
||||||
|
Subtitle string `json:"subtitle" validate:"max=1024"`
|
||||||
|
Body string `json:"content" validate:"required,max=4096"`
|
||||||
|
Metadata map[string]any `json:"metadata"`
|
||||||
|
Priority int `json:"priority"`
|
||||||
|
IsRealtime bool `json:"is_realtime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
client, err := services.GetThirdClientWithUser(data.ClientID, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("unable to get client: %v", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
var accounts []models.Account
|
||||||
|
if err := database.C.Find(&accounts).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var notifications []models.Notification
|
||||||
|
for _, account := range accounts {
|
||||||
|
notification := models.Notification{
|
||||||
|
Topic: data.Topic,
|
||||||
|
Subtitle: data.Subtitle,
|
||||||
|
Title: data.Title,
|
||||||
|
Body: data.Body,
|
||||||
|
Metadata: data.Metadata,
|
||||||
|
Priority: data.Priority,
|
||||||
|
Account: account,
|
||||||
|
AccountID: account.ID,
|
||||||
|
SenderID: &client.ID,
|
||||||
|
}
|
||||||
|
notifications = append(notifications, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
if data.IsRealtime {
|
||||||
|
go services.PushNotificationBatch(notifications)
|
||||||
|
} else {
|
||||||
|
go services.NewNotificationBatch(notifications)
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
@ -4,7 +4,7 @@ import (
|
|||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
@ -44,7 +44,7 @@ func tryAuthorizeThirdClient(c *fiber.Ctx) error {
|
|||||||
"ticket": nil,
|
"ticket": nil,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
ticket, err = services.RotateTicket(ticket)
|
ticket, _ = services.RotateTicket(ticket)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
@ -95,7 +95,9 @@ func authorizeThirdClient(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "oauth.connect", map[string]any{
|
||||||
|
"client": client,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"ticket": ticket,
|
"ticket": ticket,
|
||||||
"redirect_uri": redirect,
|
"redirect_uri": redirect,
|
||||||
@ -118,7 +120,9 @@ func authorizeThirdClient(c *fiber.Ctx) error {
|
|||||||
} else if access, refresh, err := services.GetToken(ticket); err != nil {
|
} else if access, refresh, err := services.GetToken(ticket); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "oauth.connect", client.Alias, c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "oauth.connect", map[string]any{
|
||||||
|
"client": client,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(fiber.Map{
|
return c.JSON(fiber.Map{
|
||||||
"access_token": access,
|
"access_token": access,
|
||||||
"refresh_token": refresh,
|
"refresh_token": refresh,
|
78
pkg/internal/web/api/pages_api.go
Normal file
78
pkg/internal/web/api/pages_api.go
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/sec"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getAccountPage(c *fiber.Ctx) error {
|
||||||
|
alias := c.Params("alias")
|
||||||
|
|
||||||
|
tx := database.C.Where("name = ?", alias)
|
||||||
|
|
||||||
|
numericId, err := strconv.Atoi(alias)
|
||||||
|
if err == nil {
|
||||||
|
tx = tx.Or("id = ?", numericId)
|
||||||
|
}
|
||||||
|
|
||||||
|
var account models.Account
|
||||||
|
if err := tx.First(&account).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var page models.AccountPage
|
||||||
|
if err := database.C.Where("account_id = ?", account.ID).First(&page).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOwnAccountPage(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var page models.AccountPage
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).First(&page).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(page)
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateAccountPage(c *fiber.Ctx) error {
|
||||||
|
if err := sec.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Content string `json:"content" validate:"required"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var page models.AccountPage
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).First(&page).Error; err != nil {
|
||||||
|
page = models.AccountPage{
|
||||||
|
AccountID: user.ID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
page.Content = data.Content
|
||||||
|
|
||||||
|
if err := database.C.Save(&page).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return c.JSON(page)
|
||||||
|
}
|
@ -1,47 +1,11 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func getAvailableFactors(c *fiber.Ctx) error {
|
|
||||||
ticketId := c.QueryInt("ticketId", 0)
|
|
||||||
if ticketId <= 0 {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "must provide ticket id as a query parameter")
|
|
||||||
}
|
|
||||||
|
|
||||||
ticket, err := services.GetTicket(uint(ticketId))
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("ticket was not found: %v", err))
|
|
||||||
}
|
|
||||||
factors, err := services.ListUserFactor(ticket.AccountID)
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(factors)
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestFactorToken(c *fiber.Ctx) error {
|
|
||||||
id, _ := c.ParamsInt("factorId", 0)
|
|
||||||
|
|
||||||
factor, err := services.GetFactor(uint(id))
|
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
if sent, err := services.GetFactorCode(factor); err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
} else if !sent {
|
|
||||||
return c.SendStatus(fiber.StatusNoContent)
|
|
||||||
} else {
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func requestResetPassword(c *fiber.Ctx) error {
|
func requestResetPassword(c *fiber.Ctx) error {
|
||||||
var data struct {
|
var data struct {
|
||||||
UserID uint `json:"user_id" validate:"required"`
|
UserID uint `json:"user_id" validate:"required"`
|
53
pkg/internal/web/api/perms_api.go
Normal file
53
pkg/internal/web/api/perms_api.go
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
jsoniter "github.com/json-iterator/go"
|
||||||
|
)
|
||||||
|
|
||||||
|
func checkPermission(c *fiber.Ctx) error {
|
||||||
|
var data struct {
|
||||||
|
PermNode string `json:"perm_node" validate:"required"`
|
||||||
|
Value any `json:"value" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
var heldPerms map[string]any
|
||||||
|
rawHeldPerms, _ := jsoniter.Marshal(user.PermNodes)
|
||||||
|
_ = jsoniter.Unmarshal(rawHeldPerms, &heldPerms)
|
||||||
|
valid := services.HasPermNode(heldPerms, data.PermNode, data.Value)
|
||||||
|
if !valid {
|
||||||
|
return c.SendStatus(fiber.StatusForbidden)
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkUserPermission(c *fiber.Ctx) error {
|
||||||
|
var data struct {
|
||||||
|
PermNode string `json:"perm_node" validate:"required"`
|
||||||
|
Value any `json:"value" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
relatedId, _ := c.ParamsInt("userId")
|
||||||
|
relation, err := services.GetRelationWithTwoNode(user.ID, uint(relatedId))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defaultPerm := relation.Status == models.RelationshipFriend
|
||||||
|
valid := services.HasPermNodeWithDefault(relation.PermNodes, data.PermNode, data.Value, defaultPerm)
|
||||||
|
if !valid {
|
||||||
|
return c.SendStatus(fiber.StatusForbidden)
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
@ -2,8 +2,8 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,7 +36,10 @@ func updateAuthPreference(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "preferences.edit", "auth", c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "preferences.edit", map[string]any{
|
||||||
|
"type": "auth",
|
||||||
|
"preferences": data,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(cfg.Config.Data())
|
return c.JSON(cfg.Config.Data())
|
||||||
@ -47,7 +50,7 @@ func getNotificationPreference(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
notification, err := services.GetNotificationPreference(user)
|
notification, err := services.GetNotifyPreference(user)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
@ -69,11 +72,14 @@ func updateNotificationPreference(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
notification, err := services.UpdateNotificationPreference(user, data.Config)
|
notification, err := services.UpdateNotifyPreference(user, data.Config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "preferences.edit", "notifications", c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "preferences.edit", map[string]any{
|
||||||
|
"type": "notify",
|
||||||
|
"preferences": data,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(notification)
|
return c.JSON(notification)
|
72
pkg/internal/web/api/programs_api.go
Normal file
72
pkg/internal/web/api/programs_api.go
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func listProgram(c *fiber.Ctx) error {
|
||||||
|
var programs []models.Program
|
||||||
|
if err := database.C.Find(&programs).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
return c.JSON(programs)
|
||||||
|
}
|
||||||
|
|
||||||
|
func getProgram(c *fiber.Ctx) error {
|
||||||
|
var program models.Program
|
||||||
|
programId, _ := c.ParamsInt("programId")
|
||||||
|
if err := database.C.Where("id = ?", programId).First(&program).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
return c.JSON(program)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listProgramMembership(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
var members []models.ProgramMember
|
||||||
|
if err := database.C.Where("account_id = ?", user.ID).Preload("Program").Find(&members).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
}
|
||||||
|
return c.JSON(members)
|
||||||
|
}
|
||||||
|
|
||||||
|
func joinProgram(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
programId, _ := c.ParamsInt("programId")
|
||||||
|
var program models.Program
|
||||||
|
if err := database.C.Where("id = ?", programId).First(&program).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
if member, err := services.JoinProgram(user, program); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.JSON(member)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func leaveProgram(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
programId, _ := c.ParamsInt("programId")
|
||||||
|
var program models.Program
|
||||||
|
if err := database.C.Where("id = ?", programId).First(&program).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
if err := services.LeaveProgram(user, program); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
|
} else {
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
}
|
||||||
|
}
|
180
pkg/internal/web/api/punishments_api.go
Normal file
180
pkg/internal/web/api/punishments_api.go
Normal file
@ -0,0 +1,180 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getPunishment(c *fiber.Ctx) error {
|
||||||
|
id, _ := c.ParamsInt("id")
|
||||||
|
data, err := services.GetPunishment(uint(id), true)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.JSON(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listUserPunishment(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
data, err := services.ListPunishments(user)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.JSON(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func listMadePunishment(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
moderator := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
take := c.QueryInt("take", 0)
|
||||||
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
|
if c.QueryBool("all", false) {
|
||||||
|
if err := exts.EnsureGrantedPerm(c, "OverridePunishments", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
count, err := services.CountAllPunishments()
|
||||||
|
data, err := services.ListAllPunishments(take, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
"data": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
count, err := services.CountMadePunishments(moderator)
|
||||||
|
data, err := services.ListMadePunishments(moderator, take, offset)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
"data": data,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func createPunishment(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureGrantedPerm(c, "CreatePunishments", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Reason string `json:"reason" validate:"required"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
ExpiredAt *time.Time `json:"expired_at"`
|
||||||
|
PermNodes map[string]any `json:"perm_nodes"`
|
||||||
|
AccountID uint `json:"account_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var account models.Account
|
||||||
|
if err := database.C.Where("id = ?", data.AccountID).First(&account).Error; err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
punishment := models.Punishment{
|
||||||
|
Reason: data.Reason,
|
||||||
|
Type: data.Type,
|
||||||
|
PermNodes: data.PermNodes,
|
||||||
|
ExpiredAt: data.ExpiredAt,
|
||||||
|
Account: account,
|
||||||
|
AccountID: account.ID,
|
||||||
|
}
|
||||||
|
|
||||||
|
if punishment, err := services.NewPunishment(punishment, user); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return c.JSON(punishment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func editPunishment(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
id, _ := c.ParamsInt("id", 0)
|
||||||
|
|
||||||
|
var data struct {
|
||||||
|
Reason string `json:"reason" validate:"required"`
|
||||||
|
Type int `json:"type"`
|
||||||
|
ExpiredAt *time.Time `json:"expired_at"`
|
||||||
|
PermNodes map[string]any `json:"perm_nodes"`
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var punishment models.Punishment
|
||||||
|
if c.QueryBool("override", false) {
|
||||||
|
if err = exts.EnsureGrantedPerm(c, "OverridePunishments", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
punishment, err = services.GetPunishment(uint(id))
|
||||||
|
} else {
|
||||||
|
punishment, err = services.GetMadePunishment(uint(id), user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
punishment.Reason = data.Reason
|
||||||
|
punishment.Type = data.Type
|
||||||
|
punishment.ExpiredAt = data.ExpiredAt
|
||||||
|
punishment.PermNodes = data.PermNodes
|
||||||
|
|
||||||
|
if punishment, err := services.EditPunishment(punishment); err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
return c.JSON(punishment)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deletePunishment(c *fiber.Ctx) error {
|
||||||
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
user := c.Locals("user").(models.Account)
|
||||||
|
|
||||||
|
id := c.QueryInt("id")
|
||||||
|
|
||||||
|
var err error
|
||||||
|
var punishment models.Punishment
|
||||||
|
if c.QueryBool("override", false) {
|
||||||
|
if err = exts.EnsureGrantedPerm(c, "OverridePunishments", true); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
punishment, err = services.GetPunishment(uint(id))
|
||||||
|
} else {
|
||||||
|
punishment, err = services.GetMadePunishment(uint(id), user)
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := services.DeletePunishment(punishment); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
@ -3,20 +3,28 @@ package api
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"strconv"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listRealmMembers(c *fiber.Ctx) error {
|
func listRealmMembers(c *fiber.Ctx) error {
|
||||||
alias := c.Params("realm")
|
alias := c.Params("realm")
|
||||||
|
take := c.QueryInt("take", 0)
|
||||||
|
offset := c.QueryInt("offset", 0)
|
||||||
|
|
||||||
if realm, err := services.GetRealmWithAlias(alias); err != nil {
|
if realm, err := services.GetRealmWithAlias(alias); err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
} else if members, err := services.ListRealmMember(realm.ID); err != nil {
|
} else if count, err := services.CountRealmMember(realm.ID); err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
|
} else if members, err := services.ListRealmMember(realm.ID, take, offset); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.JSON(members)
|
return c.JSON(fiber.Map{
|
||||||
|
"count": count,
|
||||||
|
"data": members,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -44,7 +52,7 @@ func addRealmMember(c *fiber.Ctx) error {
|
|||||||
alias := c.Params("realm")
|
alias := c.Params("realm")
|
||||||
|
|
||||||
var data struct {
|
var data struct {
|
||||||
Target string `json:"target" validate:"required"`
|
Related string `json:"related" validate:"required"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
@ -57,9 +65,17 @@ func addRealmMember(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var account models.Account
|
var account models.Account
|
||||||
if err := database.C.Where(&models.Account{
|
var numericId int
|
||||||
Name: data.Target,
|
if numericId, err = strconv.Atoi(data.Related); err == nil {
|
||||||
}).First(&account).Error; err != nil {
|
err = database.C.Where(&models.Account{
|
||||||
|
BaseModel: models.BaseModel{ID: uint(numericId)},
|
||||||
|
}).First(&account).Error
|
||||||
|
} else {
|
||||||
|
err = database.C.Where(&models.Account{
|
||||||
|
Name: data.Related,
|
||||||
|
}).First(&account).Error
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,28 +92,22 @@ func removeRealmMember(c *fiber.Ctx) error {
|
|||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
alias := c.Params("realm")
|
alias := c.Params("realm")
|
||||||
|
memberId, _ := c.ParamsInt("memberId", 0)
|
||||||
var data struct {
|
|
||||||
Target string `json:"target" validate:"required"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := exts.BindAndValidate(c, &data); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
realm, err := services.GetRealmWithAlias(alias)
|
realm, err := services.GetRealmWithAlias(alias)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
var account models.Account
|
var member models.RealmMember
|
||||||
if err := database.C.Where(&models.Account{
|
if err := database.C.Where(&models.RealmMember{
|
||||||
Name: data.Target,
|
BaseModel: models.BaseModel{ID: uint(memberId)},
|
||||||
}).First(&account).Error; err != nil {
|
RealmID: realm.ID,
|
||||||
|
}).First(&member).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := services.RemoveRealmMember(user, account, realm); err != nil {
|
if err := services.RemoveRealmMember(user, member, realm); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
@ -118,14 +128,15 @@ func leaveRealm(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusBadRequest, "you cannot leave your own realm")
|
return fiber.NewError(fiber.StatusBadRequest, "you cannot leave your own realm")
|
||||||
}
|
}
|
||||||
|
|
||||||
var account models.Account
|
var member models.RealmMember
|
||||||
if err := database.C.Where(&models.Account{
|
if err := database.C.Where(&models.RealmMember{
|
||||||
BaseModel: models.BaseModel{ID: user.ID},
|
RealmID: realm.ID,
|
||||||
}).First(&account).Error; err != nil {
|
AccountID: user.ID,
|
||||||
|
}).First(&member).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := services.RemoveRealmMember(user, account, realm); err != nil {
|
if err := database.C.Delete(&member).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
@ -3,10 +3,9 @@ package api
|
|||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"strconv"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func getRealm(c *fiber.Ctx) error {
|
func getRealm(c *fiber.Ctx) error {
|
||||||
@ -87,7 +86,9 @@ func createRealm(c *fiber.Ctx) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "realms.new", strconv.Itoa(int(realm.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "realms.new", map[string]any{
|
||||||
|
"realm": realm,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(realm)
|
return c.JSON(realm)
|
||||||
@ -123,6 +124,7 @@ func editRealm(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
og := realm
|
||||||
realm.Alias = data.Alias
|
realm.Alias = data.Alias
|
||||||
realm.Name = data.Name
|
realm.Name = data.Name
|
||||||
realm.Description = data.Description
|
realm.Description = data.Description
|
||||||
@ -132,11 +134,13 @@ func editRealm(c *fiber.Ctx) error {
|
|||||||
realm.IsPublic = data.IsPublic
|
realm.IsPublic = data.IsPublic
|
||||||
realm.IsCommunity = data.IsCommunity
|
realm.IsCommunity = data.IsCommunity
|
||||||
|
|
||||||
realm, err := services.EditRealm(realm)
|
realm, err := services.EditRealm(realm, og)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "realms.edit", strconv.Itoa(int(realm.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "realms.edit", map[string]any{
|
||||||
|
"realm": realm,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(realm)
|
return c.JSON(realm)
|
||||||
@ -160,7 +164,9 @@ func deleteRealm(c *fiber.Ctx) error {
|
|||||||
if err := services.DeleteRealm(realm); err != nil {
|
if err := services.DeleteRealm(realm); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "realms.delete", strconv.Itoa(int(realm.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "realms.delete", map[string]any{
|
||||||
|
"realm": realm,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
@ -1,11 +1,14 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
|
||||||
"github.com/gofiber/fiber/v2"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
|
"github.com/gofiber/fiber/v2"
|
||||||
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func listRelationship(c *fiber.Ctx) error {
|
func listRelationship(c *fiber.Ctx) error {
|
||||||
@ -13,16 +16,24 @@ func listRelationship(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
status := c.QueryInt("status", -1)
|
statusQuery := c.Query("status")
|
||||||
|
|
||||||
|
status := lo.Map(strings.Split(statusQuery, ","), func(s string, _ int) models.RelationshipStatus {
|
||||||
|
num, err := strconv.Atoi(s)
|
||||||
|
if err != nil {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return models.RelationshipStatus(num)
|
||||||
|
})
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var friends []models.AccountRelationship
|
var friends []models.AccountRelationship
|
||||||
if status < 0 {
|
if len(status) == 0 {
|
||||||
if friends, err = services.ListAllRelationship(user); err != nil {
|
if friends, err = services.ListAllRelationship(user); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if friends, err = services.ListRelationshipWithFilter(user, models.RelationshipStatus(status)); err != nil {
|
if friends, err = services.ListRelationshipWithFilter(user, status...); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,7 +87,9 @@ func editRelationship(c *fiber.Ctx) error {
|
|||||||
if friendship, err := services.EditRelationship(relationship); err != nil {
|
if friendship, err := services.EditRelationship(relationship); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "relationships.edit", strconv.Itoa(int(relationship.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "relationships.edit", map[string]any{
|
||||||
|
"relationship": relationship,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(friendship)
|
return c.JSON(friendship)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -100,7 +113,9 @@ func deleteRelationship(c *fiber.Ctx) error {
|
|||||||
if err := services.DeleteRelationship(relationship); err != nil {
|
if err := services.DeleteRelationship(relationship); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "relationships.delete", strconv.Itoa(int(relationship.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "relationships.delete", map[string]any{
|
||||||
|
"relationship": relationship,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(relationship)
|
return c.JSON(relationship)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -112,30 +127,35 @@ func makeFriendship(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
relatedName := c.Query("related")
|
|
||||||
relatedId, _ := c.ParamsInt("relatedId", 0)
|
var data struct {
|
||||||
|
Related string `json:"related" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var related models.Account
|
var related models.Account
|
||||||
if relatedId > 0 {
|
if numericId, err := strconv.Atoi(data.Related); err == nil {
|
||||||
related, err = services.GetAccount(uint(relatedId))
|
related, err = services.GetAccount(uint(numericId))
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
} else if len(relatedName) > 0 {
|
|
||||||
related, err = services.LookupAccount(relatedName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "must one of username or user id")
|
related, err = services.LookupAccount(data.Related)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
friend, err := services.NewFriend(user, related)
|
friend, err := services.NewFriend(user, related)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "relationships.friends.new", strconv.Itoa(relatedId), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "relationships.friends.new", map[string]any{
|
||||||
|
"related": related,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(friend)
|
return c.JSON(friend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,30 +165,35 @@ func makeBlockship(c *fiber.Ctx) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
user := c.Locals("user").(models.Account)
|
user := c.Locals("user").(models.Account)
|
||||||
relatedName := c.Query("related")
|
|
||||||
relatedId, _ := c.ParamsInt("relatedId", 0)
|
var data struct {
|
||||||
|
Related string `json:"related" validate:"required"`
|
||||||
|
}
|
||||||
|
if err := exts.BindAndValidate(c, &data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
var related models.Account
|
var related models.Account
|
||||||
if relatedId > 0 {
|
if numericId, err := strconv.Atoi(data.Related); err == nil {
|
||||||
related, err = services.GetAccount(uint(relatedId))
|
related, err = services.GetAccount(uint(numericId))
|
||||||
if err != nil {
|
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
|
||||||
}
|
|
||||||
} else if len(relatedName) > 0 {
|
|
||||||
related, err = services.LookupAccount(relatedName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, "must one of username or user id")
|
related, err = services.LookupAccount(data.Related)
|
||||||
|
if err != nil {
|
||||||
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
friend, err := services.NewBlockship(user, related)
|
friend, err := services.NewBlockship(user, related)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "relationships.blocks.new", strconv.Itoa(relatedId), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "relationships.blocks.new", map[string]any{
|
||||||
|
"related": related,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(friend)
|
return c.JSON(friend)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -188,7 +213,9 @@ func acceptFriend(c *fiber.Ctx) error {
|
|||||||
if err := services.HandleFriend(user, related, true); err != nil {
|
if err := services.HandleFriend(user, related, true); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "relationships.friends.accept", strconv.Itoa(relatedId), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "relationships.friends.accept", map[string]any{
|
||||||
|
"related": related,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -208,7 +235,9 @@ func declineFriend(c *fiber.Ctx) error {
|
|||||||
if err := services.HandleFriend(user, related, false); err != nil {
|
if err := services.HandleFriend(user, related, false); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "relationships.friends.decline", strconv.Itoa(relatedId), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "relationships.friends.decline", map[string]any{
|
||||||
|
"related": related,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -2,8 +2,8 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
@ -1,9 +1,12 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"git.solsynth.dev/hypernet/nexus/pkg/nex/cachekit"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/gap"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -39,7 +42,7 @@ func getTickets(c *fiber.Ctx) error {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func killTicket(c *fiber.Ctx) error {
|
func deleteTicket(c *fiber.Ctx) error {
|
||||||
if err := exts.EnsureAuthenticated(c); err != nil {
|
if err := exts.EnsureAuthenticated(c); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -51,6 +54,8 @@ func killTicket(c *fiber.Ctx) error {
|
|||||||
AccountID: user.ID,
|
AccountID: user.ID,
|
||||||
}).Error; err != nil {
|
}).Error; err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
|
} else {
|
||||||
|
cachekit.Delete(gap.Ca, services.KgAuthContextCache(uint(id)))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
@ -2,13 +2,12 @@ package api
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
"git.solsynth.dev/hypernet/passport/pkg/authkit/models"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/database"
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/http/exts"
|
|
||||||
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
"git.solsynth.dev/hypernet/passport/pkg/internal/services"
|
||||||
|
"git.solsynth.dev/hypernet/passport/pkg/internal/web/exts"
|
||||||
"github.com/gofiber/fiber/v2"
|
"github.com/gofiber/fiber/v2"
|
||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
@ -91,7 +90,9 @@ func setStatus(c *fiber.Ctx) error {
|
|||||||
if status, err := services.NewStatus(user, status); err != nil {
|
if status, err := services.NewStatus(user, status); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "statuses.set", strconv.Itoa(int(status.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "statuses.set", map[string]any{
|
||||||
|
"status": status,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(status)
|
return c.JSON(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -130,7 +131,9 @@ func editStatus(c *fiber.Ctx) error {
|
|||||||
if status, err := services.EditStatus(user, status); err != nil {
|
if status, err := services.EditStatus(user, status); err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "statuses.edit", strconv.Itoa(int(status.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "statuses.edit", map[string]any{
|
||||||
|
"status": status,
|
||||||
|
}, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
return c.JSON(status)
|
return c.JSON(status)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -144,7 +147,7 @@ func clearStatus(c *fiber.Ctx) error {
|
|||||||
if err := services.ClearStatus(user); err != nil {
|
if err := services.ClearStatus(user); err != nil {
|
||||||
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
|
||||||
} else {
|
} else {
|
||||||
services.AddEvent(user.ID, "statuses.clear", strconv.Itoa(int(user.ID)), c.IP(), c.Get(fiber.HeaderUserAgent))
|
services.AddEvent(user.ID, "statuses.clear", nil, c.IP(), c.Get(fiber.HeaderUserAgent))
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user