🗑️ Remove the personal page
This commit is contained in:
		
							
								
								
									
										71
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										71
									
								
								.idea/workspace.xml
									
									
									
										generated
									
									
									
								
							| @@ -4,20 +4,15 @@ | |||||||
|     <option name="autoReloadType" value="ALL" /> |     <option name="autoReloadType" value="ALL" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="ChangeListManager"> |   <component name="ChangeListManager"> | ||||||
|     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":ambulance: Fix nil map panic"> |     <list default="true" id="3fefb2c4-b6f9-466b-a523-53352e8d6f95" name="更改" comment=":recycle: Update the sign in web page to the latest API"> | ||||||
|       <change afterPath="$PROJECT_DIR$/web/src/components/auth/AuthenticateCompleted.vue" afterDir="false" /> |  | ||||||
|       <change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.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/auth_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/auth_api.go" afterDir="false" /> |       <change beforePath="$PROJECT_DIR$/pkg/internal/database/migrator.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/database/migrator.go" afterDir="false" /> | ||||||
|       <change beforePath="$PROJECT_DIR$/pkg/internal/server/api/factors_api.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/server/api/factors_api.go" afterDir="false" /> |       <change beforePath="$PROJECT_DIR$/pkg/internal/models/accounts.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/models/accounts.go" afterDir="false" /> | ||||||
|  |       <change beforePath="$PROJECT_DIR$/pkg/internal/models/profiles.go" beforeDir="false" afterPath="$PROJECT_DIR$/pkg/internal/models/profiles.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$/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/components/Copyright.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/components/Copyright.vue" afterDir="false" /> |       <change beforePath="$PROJECT_DIR$/pkg/internal/server/api/page_api.go" beforeDir="false" /> | ||||||
|       <change beforePath="$PROJECT_DIR$/web/src/components/auth/AccountLocator.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/components/auth/Authenticate.vue" afterDir="false" /> |  | ||||||
|       <change beforePath="$PROJECT_DIR$/web/src/components/auth/FactorApplicator.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/components/auth/FactorApplicator.vue" afterDir="false" /> |  | ||||||
|       <change beforePath="$PROJECT_DIR$/web/src/components/auth/FactorPicker.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/components/auth/FactorPicker.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/router/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/router/index.ts" afterDir="false" /> | ||||||
|       <change beforePath="$PROJECT_DIR$/web/src/views/auth/connect.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/auth/authorize.vue" afterDir="false" /> |       <change beforePath="$PROJECT_DIR$/web/src/views/personal-page.vue" beforeDir="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/confirm.vue" beforeDir="false" afterPath="$PROJECT_DIR$/web/src/views/confirm.vue" afterDir="false" /> |  | ||||||
|     </list> |     </list> | ||||||
|     <option name="SHOW_DIALOG" value="false" /> |     <option name="SHOW_DIALOG" value="false" /> | ||||||
|     <option name="HIGHLIGHT_CONFLICTS" value="true" /> |     <option name="HIGHLIGHT_CONFLICTS" value="true" /> | ||||||
| @@ -52,34 +47,34 @@ | |||||||
|     <option name="hideEmptyMiddlePackages" value="true" /> |     <option name="hideEmptyMiddlePackages" value="true" /> | ||||||
|     <option name="showLibraryContents" value="true" /> |     <option name="showLibraryContents" value="true" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="PropertiesComponent"><![CDATA[{ |   <component name="PropertiesComponent">{ | ||||||
|   "keyToString": { |   "keyToString": { | ||||||
|     "DefaultGoTemplateProperty": "Go File", |     "DefaultGoTemplateProperty": "Go File", | ||||||
|     "Go Build.Backend.executor": "Run", |     "Go Build.Backend.executor": "Run", | ||||||
|     "Go 构建.Backend.executor": "Run", |     "Go 构建.Backend.executor": "Run", | ||||||
|     "RunOnceActivity.ShowReadmeOnStart": "true", |     "RunOnceActivity.ShowReadmeOnStart": "true", | ||||||
|     "RunOnceActivity.go.formatter.settings.were.checked": "true", |     "RunOnceActivity.go.formatter.settings.were.checked": "true", | ||||||
|     "RunOnceActivity.go.migrated.go.modules.settings": "true", |     "RunOnceActivity.go.migrated.go.modules.settings": "true", | ||||||
|     "RunOnceActivity.go.modules.automatic.dependencies.download": "true", |     "RunOnceActivity.go.modules.automatic.dependencies.download": "true", | ||||||
|     "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", |     "RunOnceActivity.go.modules.go.list.on.any.changes.was.set": "true", | ||||||
|     "git-widget-placeholder": "refactor/v2", |     "git-widget-placeholder": "refactor/v2", | ||||||
|     "go.import.settings.migrated": "true", |     "go.import.settings.migrated": "true", | ||||||
|     "go.sdk.automatically.set": "true", |     "go.sdk.automatically.set": "true", | ||||||
|     "last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web", |     "last_opened_file_path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web", | ||||||
|     "node.js.detected.package.eslint": "true", |     "node.js.detected.package.eslint": "true", | ||||||
|     "node.js.selected.package.eslint": "(autodetect)", |     "node.js.selected.package.eslint": "(autodetect)", | ||||||
|     "nodejs_package_manager_path": "npm", |     "nodejs_package_manager_path": "npm", | ||||||
|     "run.code.analysis.last.selected.profile": "pProject Default", |     "run.code.analysis.last.selected.profile": "pProject Default", | ||||||
|     "settings.editor.selected.configurable": "preferences.pluginManager", |     "settings.editor.selected.configurable": "preferences.pluginManager", | ||||||
|     "ts.external.directory.path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web/node_modules/typescript/lib", |     "ts.external.directory.path": "/Users/littlesheep/Documents/Projects/Hydrogen/Passport/web/node_modules/typescript/lib", | ||||||
|     "vue.rearranger.settings.migration": "true" |     "vue.rearranger.settings.migration": "true" | ||||||
|   }, |   }, | ||||||
|   "keyToStringList": { |   "keyToStringList": { | ||||||
|     "DatabaseDriversLRU": [ |     "DatabaseDriversLRU": [ | ||||||
|       "postgresql" |       "postgresql" | ||||||
|     ] |     ] | ||||||
|   } |   } | ||||||
| }]]></component> | }</component> | ||||||
|   <component name="RecentsManager"> |   <component name="RecentsManager"> | ||||||
|     <key name="CopyFile.RECENT_KEYS"> |     <key name="CopyFile.RECENT_KEYS"> | ||||||
|       <recent name="$PROJECT_DIR$/web" /> |       <recent name="$PROJECT_DIR$/web" /> | ||||||
| @@ -155,7 +150,6 @@ | |||||||
|     </option> |     </option> | ||||||
|   </component> |   </component> | ||||||
|   <component name="VcsManagerConfiguration"> |   <component name="VcsManagerConfiguration"> | ||||||
|     <MESSAGE value=":sparkles: Bug fixes of permission check" /> |  | ||||||
|     <MESSAGE value=":sparkles: Check permissions GRPC method" /> |     <MESSAGE value=":sparkles: Check permissions GRPC method" /> | ||||||
|     <MESSAGE value=":recycle: Use paperclip to store avatar and more" /> |     <MESSAGE value=":recycle: Use paperclip to store avatar and more" /> | ||||||
|     <MESSAGE value=":bug: Bug fixes in update avatar" /> |     <MESSAGE value=":bug: Bug fixes in update avatar" /> | ||||||
| @@ -180,7 +174,8 @@ | |||||||
|     <MESSAGE value=":bug: Fix avatar url missing endpoint prefix" /> |     <MESSAGE value=":bug: Fix avatar url missing endpoint prefix" /> | ||||||
|     <MESSAGE value=":ambulance: Fix query services too much 429" /> |     <MESSAGE value=":ambulance: Fix query services too much 429" /> | ||||||
|     <MESSAGE value=":ambulance: Fix nil map panic" /> |     <MESSAGE value=":ambulance: Fix nil map panic" /> | ||||||
|     <option name="LAST_COMMIT_MESSAGE" value=":ambulance: Fix nil map panic" /> |     <MESSAGE value=":recycle: Update the sign in web page to the latest API" /> | ||||||
|  |     <option name="LAST_COMMIT_MESSAGE" value=":recycle: Update the sign in web page to the latest API" /> | ||||||
|   </component> |   </component> | ||||||
|   <component name="VgoProject"> |   <component name="VgoProject"> | ||||||
|     <settings-migrated>true</settings-migrated> |     <settings-migrated>true</settings-migrated> | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ var AutoMaintainRange = []any{ | |||||||
| 	&models.Account{}, | 	&models.Account{}, | ||||||
| 	&models.AuthFactor{}, | 	&models.AuthFactor{}, | ||||||
| 	&models.AccountProfile{}, | 	&models.AccountProfile{}, | ||||||
| 	&models.AccountPage{}, |  | ||||||
| 	&models.AccountContact{}, | 	&models.AccountContact{}, | ||||||
| 	&models.AccountFriendship{}, | 	&models.AccountFriendship{}, | ||||||
| 	&models.Badge{}, | 	&models.Badge{}, | ||||||
|   | |||||||
| @@ -21,7 +21,6 @@ type Account struct { | |||||||
| 	PermNodes   datatypes.JSONMap `json:"perm_nodes"` | 	PermNodes   datatypes.JSONMap `json:"perm_nodes"` | ||||||
|  |  | ||||||
| 	Profile         AccountProfile   `json:"profile"` | 	Profile         AccountProfile   `json:"profile"` | ||||||
| 	PersonalPage    AccountPage      `json:"personal_page"` |  | ||||||
| 	Badges          []Badge          `json:"badges"` | 	Badges          []Badge          `json:"badges"` | ||||||
| 	Contacts        []AccountContact `json:"contacts"` | 	Contacts        []AccountContact `json:"contacts"` | ||||||
| 	RealmIdentities []RealmMember    `json:"realm_identities"` | 	RealmIdentities []RealmMember    `json:"realm_identities"` | ||||||
|   | |||||||
| @@ -1,7 +1,6 @@ | |||||||
| package models | package models | ||||||
|  |  | ||||||
| import ( | import ( | ||||||
| 	"gorm.io/datatypes" |  | ||||||
| 	"time" | 	"time" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| @@ -14,18 +13,3 @@ type AccountProfile struct { | |||||||
| 	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"` |  | ||||||
| 	Script    string                                `json:"script"` |  | ||||||
| 	Style     string                                `json:"style"` |  | ||||||
| 	Links     datatypes.JSONSlice[AccountPageLinks] `json:"links"` |  | ||||||
| 	AccountID uint                                  `json:"account_id"` |  | ||||||
| } |  | ||||||
|  |  | ||||||
| type AccountPageLinks struct { |  | ||||||
| 	Label string `json:"label"` |  | ||||||
| 	Url   string `json:"url"` |  | ||||||
| } |  | ||||||
|   | |||||||
| @@ -26,9 +26,7 @@ func MapAPIs(app *fiber.App) { | |||||||
| 			me.Put("/banner", setBanner) | 			me.Put("/banner", setBanner) | ||||||
|  |  | ||||||
| 			me.Get("/", getUserinfo) | 			me.Get("/", getUserinfo) | ||||||
| 			me.Get("/page", getOwnPersonalPage) |  | ||||||
| 			me.Put("/", editUserinfo) | 			me.Put("/", editUserinfo) | ||||||
| 			me.Put("/page", editPersonalPage) |  | ||||||
| 			me.Get("/events", getEvents) | 			me.Get("/events", getEvents) | ||||||
| 			me.Get("/tickets", getTickets) | 			me.Get("/tickets", getTickets) | ||||||
| 			me.Delete("/tickets/:ticketId", killSession) | 			me.Delete("/tickets/:ticketId", killSession) | ||||||
| @@ -49,7 +47,6 @@ func MapAPIs(app *fiber.App) { | |||||||
| 		directory := api.Group("/users/:alias").Name("User Directory") | 		directory := api.Group("/users/:alias").Name("User Directory") | ||||||
| 		{ | 		{ | ||||||
| 			directory.Get("/", getOtherUserinfo) | 			directory.Get("/", getOtherUserinfo) | ||||||
| 			directory.Get("/page", getPersonalPage) |  | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		api.Post("/users", doRegister) | 		api.Post("/users", doRegister) | ||||||
|   | |||||||
| @@ -1,76 +0,0 @@ | |||||||
| package api |  | ||||||
|  |  | ||||||
| import ( |  | ||||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/database" |  | ||||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/models" |  | ||||||
| 	"git.solsynth.dev/hydrogen/passport/pkg/internal/server/exts" |  | ||||||
| 	"github.com/gofiber/fiber/v2" |  | ||||||
| ) |  | ||||||
|  |  | ||||||
| func getPersonalPage(c *fiber.Ctx) error { |  | ||||||
| 	alias := c.Params("alias") |  | ||||||
|  |  | ||||||
| 	var account models.Account |  | ||||||
| 	if err := database.C. |  | ||||||
| 		Where(&models.Account{Name: alias}). |  | ||||||
| 		First(&account).Error; err != nil { |  | ||||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var page models.AccountPage |  | ||||||
| 	if err := database.C. |  | ||||||
| 		Where(&models.AccountPage{AccountID: account.ID}). |  | ||||||
| 		First(&page).Error; err != nil { |  | ||||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return c.JSON(page) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func getOwnPersonalPage(c *fiber.Ctx) error { |  | ||||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	user := c.Locals("user").(models.Account) |  | ||||||
|  |  | ||||||
| 	var page models.AccountPage |  | ||||||
| 	if err := database.C. |  | ||||||
| 		Where(&models.AccountPage{AccountID: user.ID}). |  | ||||||
| 		FirstOrCreate(&page, &models.AccountPage{AccountID: user.ID}).Error; err != nil { |  | ||||||
| 		return fiber.NewError(fiber.StatusBadRequest, err.Error()) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return c.JSON(page) |  | ||||||
| } |  | ||||||
|  |  | ||||||
| func editPersonalPage(c *fiber.Ctx) error { |  | ||||||
| 	if err := exts.EnsureAuthenticated(c); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
| 	user := c.Locals("user").(models.Account) |  | ||||||
|  |  | ||||||
| 	var data struct { |  | ||||||
| 		Content string                    `json:"content"` |  | ||||||
| 		Links   []models.AccountPageLinks `json:"links"` |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	if err := exts.BindAndValidate(c, &data); err != nil { |  | ||||||
| 		return err |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	var page models.AccountPage |  | ||||||
| 	if err := database.C. |  | ||||||
| 		Where(&models.AccountPage{AccountID: user.ID}). |  | ||||||
| 		FirstOrInit(&page).Error; err != nil { |  | ||||||
| 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	page.Content = data.Content |  | ||||||
| 	page.Links = data.Links |  | ||||||
|  |  | ||||||
| 	if err := database.C.Save(&page).Error; err != nil { |  | ||||||
| 		return fiber.NewError(fiber.StatusInternalServerError, err.Error()) |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	return c.SendStatus(fiber.StatusOK) |  | ||||||
| } |  | ||||||
| @@ -6,7 +6,6 @@ | |||||||
|           <v-list density="comfortable"> |           <v-list density="comfortable"> | ||||||
|             <v-list-item title="Dashboard" prepend-icon="mdi-view-dashboard" :to="{ name: 'dashboard' }" exact /> |             <v-list-item title="Dashboard" prepend-icon="mdi-view-dashboard" :to="{ name: 'dashboard' }" exact /> | ||||||
|             <v-list-item title="Personalize" prepend-icon="mdi-card-bulleted-outline" :to="{ name: 'personalize' }" /> |             <v-list-item title="Personalize" prepend-icon="mdi-card-bulleted-outline" :to="{ name: 'personalize' }" /> | ||||||
|             <v-list-item title="Personal Page" prepend-icon="mdi-sitemap" :to="{ name: 'personal-page' }" /> |  | ||||||
|             <v-list-item title="Security" prepend-icon="mdi-security" :to="{ name: 'security' }" /> |             <v-list-item title="Security" prepend-icon="mdi-security" :to="{ name: 'security' }" /> | ||||||
|           </v-list> |           </v-list> | ||||||
|         </v-card> |         </v-card> | ||||||
|   | |||||||
| @@ -30,12 +30,6 @@ const router = createRouter({ | |||||||
|               component: () => import("@/views/personalize.vue"), |               component: () => import("@/views/personalize.vue"), | ||||||
|               meta: { title: "Your personality" }, |               meta: { title: "Your personality" }, | ||||||
|             }, |             }, | ||||||
|             { |  | ||||||
|               path: "/me/personal-page", |  | ||||||
|               name: "personal-page", |  | ||||||
|               component: () => import("@/views/personal-page.vue"), |  | ||||||
|               meta: { title: "Your personal page" }, |  | ||||||
|             }, |  | ||||||
|             { |             { | ||||||
|               path: "/me/security", |               path: "/me/security", | ||||||
|               name: "security", |               name: "security", | ||||||
|   | |||||||
| @@ -1,71 +0,0 @@ | |||||||
| <template> |  | ||||||
|   <div> |  | ||||||
|     <v-card class="mb-3" title="Design" prepend-icon="mdi-pencil-ruler" :loading="loading"> |  | ||||||
|       <template #text> |  | ||||||
|         <v-form class="mt-1" @submit.prevent="submit"> |  | ||||||
|           <v-row dense> |  | ||||||
|             <v-col :cols="12"> |  | ||||||
|               <v-textarea hide-details label="Content" density="comfortable" variant="outlined" |  | ||||||
|                           v-model="data.content" /> |  | ||||||
|             </v-col> |  | ||||||
|           </v-row> |  | ||||||
|  |  | ||||||
|           <v-btn type="submit" class="mt-2" variant="text" prepend-icon="mdi-content-save" :disabled="loading"> |  | ||||||
|             Apply Changes |  | ||||||
|           </v-btn> |  | ||||||
|         </v-form> |  | ||||||
|       </template> |  | ||||||
|     </v-card> |  | ||||||
|  |  | ||||||
|     <v-snackbar v-model="done" :timeout="3000"> Your personal page has been updated.</v-snackbar> |  | ||||||
|  |  | ||||||
|     <!-- @vue-ignore --> |  | ||||||
|     <v-snackbar v-model="error" :timeout="5000">Something went wrong... {{ error }}</v-snackbar> |  | ||||||
|   </div> |  | ||||||
| </template> |  | ||||||
|  |  | ||||||
| <script setup lang="ts"> |  | ||||||
| import { ref } from "vue"; |  | ||||||
| import { getAtk } from "@/stores/userinfo"; |  | ||||||
| import { request } from "@/scripts/request"; |  | ||||||
|  |  | ||||||
| const error = ref<string | null>(null); |  | ||||||
| const done = ref(false); |  | ||||||
| const loading = ref(false); |  | ||||||
|  |  | ||||||
| const data = ref<any>({}); |  | ||||||
|  |  | ||||||
| async function read() { |  | ||||||
|   loading.value = true; |  | ||||||
|   const res = await request("/api/users/me/page", { |  | ||||||
|     headers: { Authorization: `Bearer ${(getAtk())}` } |  | ||||||
|   }); |  | ||||||
|   if (res.status !== 200) { |  | ||||||
|     error.value = await res.text(); |  | ||||||
|   } else { |  | ||||||
|     data.value = await res.json(); |  | ||||||
|   } |  | ||||||
|   loading.value = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| async function submit() { |  | ||||||
|   const payload = data.value; |  | ||||||
|  |  | ||||||
|   loading.value = true; |  | ||||||
|   const res = await request("/api/users/me/page", { |  | ||||||
|     method: "PUT", |  | ||||||
|     headers: { "Content-Type": "application/json", Authorization: `Bearer ${getAtk()}` }, |  | ||||||
|     body: JSON.stringify(payload) |  | ||||||
|   }); |  | ||||||
|   if (res.status !== 200) { |  | ||||||
|     error.value = await res.text(); |  | ||||||
|   } else { |  | ||||||
|     await read(); |  | ||||||
|     done.value = true; |  | ||||||
|     error.value = null; |  | ||||||
|   } |  | ||||||
|   loading.value = false; |  | ||||||
| } |  | ||||||
|  |  | ||||||
| read(); |  | ||||||
| </script> |  | ||||||
		Reference in New Issue
	
	Block a user