Compare commits
4 Commits
f69f1026d8
...
38ee30f455
| Author | SHA1 | Date | |
|---|---|---|---|
| 38ee30f455 | |||
| ebdb6f5688 | |||
| ab5130de2a | |||
| 7aef153bf3 |
2
.idea/dataSources.local.xml
generated
2
.idea/dataSources.local.xml
generated
@@ -2,7 +2,7 @@
|
||||
<project version="4">
|
||||
<component name="dataSourceStorageLocal" created-in="GO-241.18034.61">
|
||||
<data-source name="hy_passport@localhost" uuid="74bcf3ef-a2b9-435b-b9e5-f32902a33b25">
|
||||
<database-info product="PostgreSQL" version="16.2 (Homebrew)" 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">
|
||||
<database-info product="PostgreSQL" version="16.3 (Homebrew)" jdbc-version="4.2" driver-name="PostgreSQL JDBC Driver" driver-version="42.6.0" dbms="POSTGRES" exact-version="16.3" exact-driver-version="42.6">
|
||||
<identifier-quote-string>"</identifier-quote-string>
|
||||
</database-info>
|
||||
<case-sensitivity plain-identifiers="lower" quoted-identifiers="exact" />
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
4||10|C|G
|
||||
4||10|T|G</Grants>
|
||||
<IntrospectionStateNumber>7430</IntrospectionStateNumber>
|
||||
<ServerVersion>16.2</ServerVersion>
|
||||
<ServerVersion>16.3</ServerVersion>
|
||||
<StartupTime>1716108620</StartupTime>
|
||||
<TimeZones>true ACDT
|
||||
true ACSST
|
||||
|
||||
23
.idea/workspace.xml
generated
23
.idea/workspace.xml
generated
@@ -4,11 +4,16 @@
|
||||
<option name="autoReloadType" value="ALL" />
|
||||
</component>
|
||||
<component name="ChangeListManager">
|
||||
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":sparkles: Better avatar and banner APIs">
|
||||
<list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":bug: Authenticate wrong payload hotfix">
|
||||
<change beforePath="$PROJECT_DIR$/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/dataSources/74bcf3ef-a2b9-435b-b9e5-f32902a33b25.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/avatar_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/avatar_api.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/components/UserMenu.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/components/UserMenu.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/views/dashboard.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/dashboard.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/pkg/internal/server/api/index.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/index.go" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/layouts/master.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/layouts/master.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/layouts/user-center.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/layouts/user-center.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/router/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/router/index.ts" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/views/auth/sign-in.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/auth/sign-in.vue" afterDir="false" />
|
||||
<change beforePath="$PROJECT_DIR$/web/src/views/personalize.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/personalize.vue" afterDir="false" />
|
||||
</list>
|
||||
<option name="SHOW_DIALOG" value="false" />
|
||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||
@@ -146,10 +151,6 @@
|
||||
</option>
|
||||
</component>
|
||||
<component name="VcsManagerConfiguration">
|
||||
<MESSAGE value=":bug: Fix APNs non-production" />
|
||||
<MESSAGE value=":bug: Bug fixes on notification badges for APNs" />
|
||||
<MESSAGE value=":bug: Fix APNs pushes no sound" />
|
||||
<MESSAGE value=":bug: Fix APNs pushes no sound (again)" />
|
||||
<MESSAGE value=":fire: Remove ws connected does not push notify feature" />
|
||||
<MESSAGE value=":sparkles: Able to read current user's realm profile" />
|
||||
<MESSAGE value=":sparkles: Consul registration" />
|
||||
@@ -171,7 +172,11 @@
|
||||
<MESSAGE value=":arrow_up: Fix notification listen" />
|
||||
<MESSAGE value=":bug: Fix magic token's foreign key" />
|
||||
<MESSAGE value=":sparkles: Better avatar and banner APIs" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value=":sparkles: Better avatar and banner APIs" />
|
||||
<MESSAGE value=":bug: Fix avatar and banner APIs" />
|
||||
<MESSAGE value=":bug: Fix frontend" />
|
||||
<MESSAGE value=":card_file_box: Add the status model" />
|
||||
<MESSAGE value=":bug: Authenticate wrong payload hotfix" />
|
||||
<option name="LAST_COMMIT_MESSAGE" value=":bug: Authenticate wrong payload hotfix" />
|
||||
</component>
|
||||
<component name="VgoProject">
|
||||
<settings-migrated>true</settings-migrated>
|
||||
|
||||
@@ -11,6 +11,7 @@ var AutoMaintainRange = []any{
|
||||
&models.AccountProfile{},
|
||||
&models.AccountContact{},
|
||||
&models.AccountFriendship{},
|
||||
&models.Status{},
|
||||
&models.Badge{},
|
||||
&models.Realm{},
|
||||
&models.RealmMember{},
|
||||
|
||||
@@ -20,8 +20,10 @@ type Account struct {
|
||||
ConfirmedAt *time.Time `json:"confirmed_at"`
|
||||
PermNodes datatypes.JSONMap `json:"perm_nodes"`
|
||||
|
||||
Profile AccountProfile `json:"profile"`
|
||||
Badges []Badge `json:"badges"`
|
||||
Profile AccountProfile `json:"profile"`
|
||||
Statuses []Status `json:"statuses"`
|
||||
Badges []Badge `json:"badges"`
|
||||
|
||||
Contacts []AccountContact `json:"contacts"`
|
||||
RealmIdentities []RealmMember `json:"realm_identities"`
|
||||
|
||||
|
||||
20
pkg/internal/models/statuses.go
Normal file
20
pkg/internal/models/statuses.go
Normal file
@@ -0,0 +1,20 @@
|
||||
package models
|
||||
|
||||
type StatusAttitude = uint8
|
||||
|
||||
const (
|
||||
AttitudeNeutral = StatusAttitude(iota)
|
||||
AttitudePositive
|
||||
AttitudeNegative
|
||||
)
|
||||
|
||||
type Status struct {
|
||||
BaseModel
|
||||
|
||||
Type string `json:"type"`
|
||||
Label string `json:"label"`
|
||||
Attitude StatusAttitude `json:"attitude"`
|
||||
IsNoDisturb bool `json:"is_no_disturb"`
|
||||
IsInvisible bool `json:"is_invisible"`
|
||||
AccountID uint `json:"account_id"`
|
||||
}
|
||||
@@ -10,9 +10,23 @@ import (
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/internal/services"
|
||||
)
|
||||
|
||||
func getTicket(c *fiber.Ctx) error {
|
||||
ticketId, err := c.ParamsInt("ticketId")
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, "ticket id is required")
|
||||
}
|
||||
|
||||
ticket, err := services.GetTicket(uint(ticketId))
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("ticket %d not found", ticketId))
|
||||
} else {
|
||||
return c.JSON(ticket)
|
||||
}
|
||||
}
|
||||
|
||||
func doAuthenticate(c *fiber.Ctx) error {
|
||||
var data struct {
|
||||
Username string `json:"username"`
|
||||
Username string `json:"username" validate:"required"`
|
||||
Password string `json:"password" validate:"required"`
|
||||
}
|
||||
|
||||
@@ -34,7 +48,7 @@ func doAuthenticate(c *fiber.Ctx) error {
|
||||
|
||||
ticket, err = services.ActiveTicketWithPassword(ticket, data.Password)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid password: %v", err.Error()))
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to authenticate: %v", err.Error()))
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
@@ -66,7 +80,7 @@ func doMultiFactorAuthenticate(c *fiber.Ctx) error {
|
||||
|
||||
ticket, err = services.ActiveTicketWithMFA(ticket, factor, data.Code)
|
||||
if err != nil {
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("invalid code: %v", err.Error()))
|
||||
return fiber.NewError(fiber.StatusBadRequest, fmt.Sprintf("failed to authenticate: %v", err.Error()))
|
||||
}
|
||||
|
||||
return c.JSON(fiber.Map{
|
||||
|
||||
@@ -59,6 +59,8 @@ func MapAPIs(app *fiber.App) {
|
||||
auth.Post("/mfa", doMultiFactorAuthenticate)
|
||||
auth.Post("/token", getToken)
|
||||
|
||||
auth.Get("/tickets/:ticketId", getTicket)
|
||||
|
||||
auth.Get("/factors", getAvailableFactors)
|
||||
auth.Post("/factors/:factorId", requestFactorToken)
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package services
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/samber/lo"
|
||||
"strings"
|
||||
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/internal/database"
|
||||
"git.solsynth.dev/hydrogen/passport/pkg/internal/models"
|
||||
@@ -99,7 +100,7 @@ func CheckFactor(factor models.AuthFactor, code string) error {
|
||||
)
|
||||
case models.EmailPasswordFactor:
|
||||
return lo.Ternary(
|
||||
code == factor.Secret,
|
||||
strings.ToUpper(code) == strings.ToUpper(factor.Secret),
|
||||
nil,
|
||||
fmt.Errorf("invalid verification code"),
|
||||
)
|
||||
|
||||
@@ -49,7 +49,7 @@ async function submit() {
|
||||
const res = await request("/api/auth", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ id: probe.value, password: password.value }),
|
||||
body: JSON.stringify({ username: probe.value, password: password.value }),
|
||||
})
|
||||
if (res.status !== 200) {
|
||||
error.value = await res.text()
|
||||
|
||||
@@ -32,8 +32,7 @@ async function load() {
|
||||
emits("update:loading", true)
|
||||
await getToken(props.ticket.grant_token)
|
||||
await userinfo.readProfiles()
|
||||
emits("update:loading", false)
|
||||
setTimeout(() => callback(), 3000)
|
||||
setTimeout(() => callback(), 1850)
|
||||
}
|
||||
|
||||
onMounted(() => load())
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppBar />
|
||||
<app-bar />
|
||||
|
||||
<v-main>
|
||||
<router-view />
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<template>
|
||||
<AppBar>
|
||||
<app-bar>
|
||||
<template #extension>
|
||||
<v-tabs align-tabs="title" color="white">
|
||||
<v-tab text="Dashboard" prepend-icon="mdi-view-dashboard" :to="{ name: 'dashboard' }" exact />
|
||||
@@ -7,7 +7,7 @@
|
||||
<v-tab text="Security" prepend-icon="mdi-security" :to="{ name: 'security' }" exact />
|
||||
</v-tabs>
|
||||
</template>
|
||||
</AppBar>
|
||||
</app-bar>
|
||||
|
||||
<v-main>
|
||||
<v-container class="pt-6 px-6 p-container">
|
||||
|
||||
@@ -39,6 +39,7 @@ const router = createRouter({
|
||||
children: [
|
||||
{
|
||||
path: "/sign-in",
|
||||
alias: ["/mfa"],
|
||||
name: "auth.sign-in",
|
||||
component: () => import("@/views/auth/sign-in.vue"),
|
||||
meta: { public: true, title: "Sign in" },
|
||||
|
||||
@@ -25,7 +25,8 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { type Component, ref } from "vue"
|
||||
import { type Component, onMounted, ref } from "vue"
|
||||
import { useRoute } from "vue-router"
|
||||
import Copyright from "@/components/Copyright.vue"
|
||||
import CallbackNotify from "@/components/auth/CallbackNotify.vue"
|
||||
import FactorPicker from "@/components/auth/FactorPicker.vue"
|
||||
@@ -33,11 +34,28 @@ import FactorApplicator from "@/components/auth/FactorApplicator.vue"
|
||||
import AccountAuthenticate from "@/components/auth/Authenticate.vue"
|
||||
import AuthenticateCompleted from "@/components/auth/AuthenticateCompleted.vue"
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const currentFactor = ref<any>(null)
|
||||
const ticket = ref<any>(null)
|
||||
|
||||
async function pickUpTicket() {
|
||||
if (route.query["ticketId"]) {
|
||||
loading.value = true
|
||||
const res = await fetch(`/api/auth/tickets/${route.query["ticketId"]}`)
|
||||
if (res.status == 200) {
|
||||
ticket.value = await res.json()
|
||||
if (ticket.value["available_at"] != null) panel.value = "completed"
|
||||
else panel.value = "mfa"
|
||||
}
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => pickUpTicket())
|
||||
|
||||
const panel = ref("authenticate")
|
||||
|
||||
const panels: { [id: string]: Component } = {
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
<template>
|
||||
<div>
|
||||
<v-card>
|
||||
<v-img cover class="bg-grey-lighten-2" :height="240" src="/api/users/me/avatar" />
|
||||
<v-img cover class="bg-grey-lighten-2" :height="240" src="/api/users/me/banner" />
|
||||
|
||||
<v-card-text class="flex gap-3.5 px-5 pb-5">
|
||||
<v-avatar
|
||||
color="grey-lighten-2"
|
||||
icon="mdi-account-circle"
|
||||
class="rounded-card"
|
||||
image="/api/users/me/banner"
|
||||
image="/api/users/me/avatar"
|
||||
:size="54"
|
||||
/>
|
||||
<div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<GoUseSolian class="mb-3" />
|
||||
<go-use-solian class="mb-3" />
|
||||
|
||||
<v-card title="Information" prepend-icon="mdi-face-man-profile" :loading="loading">
|
||||
<template #text>
|
||||
|
||||
Reference in New Issue
Block a user