diff --git a/pkg/server/accounts_api.go b/pkg/server/accounts_api.go
index 30947ca..738f296 100644
--- a/pkg/server/accounts_api.go
+++ b/pkg/server/accounts_api.go
@@ -1,15 +1,16 @@
package server
import (
+ "fmt"
+ "strconv"
+ "time"
+
"code.smartsheep.studio/hydrogen/identity/pkg/database"
"code.smartsheep.studio/hydrogen/identity/pkg/models"
"code.smartsheep.studio/hydrogen/identity/pkg/services"
- "fmt"
"github.com/gofiber/fiber/v2"
jsoniter "github.com/json-iterator/go"
"github.com/spf13/viper"
- "strconv"
- "time"
)
func getUserinfo(c *fiber.Ctx) error {
@@ -20,7 +21,6 @@ func getUserinfo(c *fiber.Ctx) error {
Where(&models.Account{BaseModel: models.BaseModel{ID: user.ID}}).
Preload("Profile").
Preload("Contacts").
- Preload("Notifications", "read_at IS NULL").
First(&data).Error; err != nil {
return fiber.NewError(fiber.StatusInternalServerError, err.Error())
}
diff --git a/pkg/server/notifications_api.go b/pkg/server/notifications_api.go
index c8f9cdf..1e84b38 100644
--- a/pkg/server/notifications_api.go
+++ b/pkg/server/notifications_api.go
@@ -1,12 +1,13 @@
package server
import (
+ "time"
+
"code.smartsheep.studio/hydrogen/identity/pkg/database"
"code.smartsheep.studio/hydrogen/identity/pkg/models"
"code.smartsheep.studio/hydrogen/identity/pkg/services"
"github.com/gofiber/fiber/v2"
"github.com/samber/lo"
- "time"
)
func getNotifications(c *fiber.Ctx) error {
@@ -14,7 +15,7 @@ func getNotifications(c *fiber.Ctx) error {
take := c.QueryInt("take", 0)
offset := c.QueryInt("offset", 0)
- only_unread := c.QueryBool("only_unread", true)
+ only_unread := !c.QueryBool("past", false)
tx := database.C.Where(&models.Notification{RecipientID: user.ID}).Model(&models.Notification{})
if only_unread {
diff --git a/pkg/services/factors.go b/pkg/services/factors.go
index 15b066c..21f538a 100644
--- a/pkg/services/factors.go
+++ b/pkg/services/factors.go
@@ -1,9 +1,10 @@
package services
import (
+ "fmt"
+
"code.smartsheep.studio/hydrogen/identity/pkg/database"
"code.smartsheep.studio/hydrogen/identity/pkg/models"
- "fmt"
"github.com/google/uuid"
"github.com/spf13/viper"
)
@@ -51,7 +52,7 @@ func GetFactorCode(factor models.AuthFactor) (bool, error) {
return true, err
}
- factor.Secret = uuid.NewString()[:8]
+ factor.Secret = uuid.NewString()[:6]
if err := database.C.Save(&factor).Error; err != nil {
return true, err
}
diff --git a/pkg/views/.eslintrc.cjs b/pkg/views/.eslintrc.cjs
index d6c9537..f41350a 100644
--- a/pkg/views/.eslintrc.cjs
+++ b/pkg/views/.eslintrc.cjs
@@ -1,18 +1,15 @@
+/* eslint-env node */
+require("@rushstack/eslint-patch/modern-module-resolution")
+
module.exports = {
root: true,
- env: { browser: true, es2020: true },
extends: [
- 'eslint:recommended',
- 'plugin:@typescript-eslint/recommended',
- 'plugin:react-hooks/recommended',
+ "plugin:vue/vue3-essential",
+ "eslint:recommended",
+ "@vue/eslint-config-typescript",
+ "@vue/eslint-config-prettier/skip-formatting",
],
- ignorePatterns: ['dist', '.eslintrc.cjs'],
- parser: '@typescript-eslint/parser',
- plugins: ['react-refresh'],
- rules: {
- 'react-refresh/only-export-components': [
- 'warn',
- { allowConstantExport: true },
- ],
+ parserOptions: {
+ ecmaVersion: "latest",
},
}
diff --git a/pkg/views/.eslintrc.js b/pkg/views/.eslintrc.js
new file mode 100644
index 0000000..b2d47e8
--- /dev/null
+++ b/pkg/views/.eslintrc.js
@@ -0,0 +1,6 @@
+module.exports = {
+ extends: ["plugin:vue/vue3-recommended"],
+ rules: {
+ "vue/multi-word-component-names": "off",
+ },
+}
diff --git a/pkg/views/.gitignore b/pkg/views/.gitignore
index a547bf3..8ee54e8 100644
--- a/pkg/views/.gitignore
+++ b/pkg/views/.gitignore
@@ -8,17 +8,23 @@ pnpm-debug.log*
lerna-debug.log*
node_modules
+.DS_Store
dist
dist-ssr
+coverage
*.local
+/cypress/videos/
+/cypress/screenshots/
+
# Editor directories and files
.vscode/*
!.vscode/extensions.json
.idea
-.DS_Store
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
+
+*.tsbuildinfo
diff --git a/pkg/views/.prettierrc.json b/pkg/views/.prettierrc.json
new file mode 100644
index 0000000..6404b10
--- /dev/null
+++ b/pkg/views/.prettierrc.json
@@ -0,0 +1,8 @@
+{
+ "$schema": "https://json.schemastore.org/prettierrc",
+ "semi": false,
+ "tabWidth": 2,
+ "singleQuote": false,
+ "printWidth": 120,
+ "trailingComma": "all"
+}
diff --git a/pkg/views/.vscode/extensions.json b/pkg/views/.vscode/extensions.json
new file mode 100644
index 0000000..0449b97
--- /dev/null
+++ b/pkg/views/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["Vue.volar", "dbaeumer.vscode-eslint", "esbenp.prettier-vscode"]
+}
diff --git a/pkg/views/README.md b/pkg/views/README.md
index 0d6babe..bdb10ae 100644
--- a/pkg/views/README.md
+++ b/pkg/views/README.md
@@ -1,30 +1,39 @@
-# React + TypeScript + Vite
+# views
-This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.
+This template should help get you started developing with Vue 3 in Vite.
-Currently, two official plugins are available:
+## Recommended IDE Setup
-- [@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/README.md) uses [Babel](https://babeljs.io/) for Fast Refresh
-- [@vitejs/plugin-react-swc](https://github.com/vitejs/vite-plugin-react-swc) uses [SWC](https://swc.rs/) for Fast Refresh
+[VSCode](https://code.visualstudio.com/) + [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) (and disable Vetur).
-## Expanding the ESLint configuration
+## Type Support for `.vue` Imports in TS
-If you are developing a production application, we recommend updating the configuration to enable type aware lint rules:
+TypeScript cannot handle type information for `.vue` imports by default, so we replace the `tsc` CLI with `vue-tsc` for type checking. In editors, we need [Volar](https://marketplace.visualstudio.com/items?itemName=Vue.volar) to make the TypeScript language service aware of `.vue` types.
-- Configure the top-level `parserOptions` property like this:
+## Customize configuration
-```js
-export default {
- // other rules...
- parserOptions: {
- ecmaVersion: 'latest',
- sourceType: 'module',
- project: ['./tsconfig.json', './tsconfig.node.json'],
- tsconfigRootDir: __dirname,
- },
-}
+See [Vite Configuration Reference](https://vitejs.dev/config/).
+
+## Project Setup
+
+```sh
+npm install
```
-- Replace `plugin:@typescript-eslint/recommended` to `plugin:@typescript-eslint/recommended-type-checked` or `plugin:@typescript-eslint/strict-type-checked`
-- Optionally add `plugin:@typescript-eslint/stylistic-type-checked`
-- Install [eslint-plugin-react](https://github.com/jsx-eslint/eslint-plugin-react) and add `plugin:react/recommended` & `plugin:react/jsx-runtime` to the `extends` list
+### Compile and Hot-Reload for Development
+
+```sh
+npm run dev
+```
+
+### Type-Check, Compile and Minify for Production
+
+```sh
+npm run build
+```
+
+### Lint with [ESLint](https://eslint.org/)
+
+```sh
+npm run lint
+```
diff --git a/pkg/views/bun.lockb b/pkg/views/bun.lockb
index b20726d..5f8e772 100755
Binary files a/pkg/views/bun.lockb and b/pkg/views/bun.lockb differ
diff --git a/pkg/views/src/vite-env.d.ts b/pkg/views/env.d.ts
similarity index 100%
rename from pkg/views/src/vite-env.d.ts
rename to pkg/views/env.d.ts
diff --git a/pkg/views/index.html b/pkg/views/index.html
index 346559b..15de511 100644
--- a/pkg/views/index.html
+++ b/pkg/views/index.html
@@ -2,12 +2,12 @@
-
+
- Goatpass
+ Solarpass
-
-
+
+
diff --git a/pkg/views/package.json b/pkg/views/package.json
index 61e5ddc..c78c836 100644
--- a/pkg/views/package.json
+++ b/pkg/views/package.json
@@ -1,47 +1,43 @@
{
- "name": "identity-web",
- "private": true,
+ "name": "views",
"version": "0.0.0",
+ "private": true,
"type": "module",
"scripts": {
"dev": "vite",
- "build": "tsc && vite build",
- "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
- "preview": "vite preview"
+ "build": "run-p type-check \"build-only {@}\" --",
+ "preview": "vite preview",
+ "build-only": "vite build",
+ "type-check": "vue-tsc --build --force",
+ "lint": "eslint . --ext .vue,.js,.jsx,.cjs,.mjs,.ts,.tsx,.cts,.mts --fix --ignore-path .gitignore",
+ "format": "prettier --write src/"
},
"dependencies": {
- "@emotion/react": "^11.11.3",
- "@emotion/styled": "^11.11.0",
- "@fontsource/roboto": "^5.0.8",
- "@mui/icons-material": "^5.15.10",
- "@mui/lab": "^5.0.0-alpha.166",
- "@mui/material": "^5.15.10",
- "@mui/x-data-grid": "^6.19.5",
- "@mui/x-date-pickers": "^6.19.5",
+ "@fontsource/roboto": "^5.0.12",
+ "@mdi/font": "^7.4.47",
"@unocss/reset": "^0.58.5",
- "dayjs": "^1.11.10",
- "localforage": "^1.10.0",
- "match-sorter": "^6.3.4",
- "react": "^18.2.0",
- "react-dom": "^18.2.0",
- "react-router-dom": "^6.22.1",
- "react-transition-group": "^4.4.5",
- "sort-by": "^1.2.0",
+ "pinia": "^2.1.7",
"universal-cookie": "^7.1.0",
- "use-debounce": "^10.0.0"
+ "unocss": "^0.58.5",
+ "vue": "^3.4.21",
+ "vue-router": "^4.3.0",
+ "vuetify": "^3.5.8"
},
"devDependencies": {
- "@types/node": "^20.11.20",
- "@types/react": "^18.2.56",
- "@types/react-dom": "^18.2.19",
- "@typescript-eslint/eslint-plugin": "^7.0.2",
- "@typescript-eslint/parser": "^7.0.2",
- "@vitejs/plugin-react-swc": "^3.5.0",
- "eslint": "^8.56.0",
- "eslint-plugin-react-hooks": "^4.6.0",
- "eslint-plugin-react-refresh": "^0.4.5",
- "typescript": "^5.2.2",
- "unocss": "^0.58.5",
- "vite": "^5.1.4"
+ "@rushstack/eslint-patch": "^1.3.3",
+ "@tsconfig/node20": "^20.1.2",
+ "@types/node": "^20.11.25",
+ "@vitejs/plugin-vue": "^5.0.4",
+ "@vitejs/plugin-vue-jsx": "^3.1.0",
+ "@vue/eslint-config-prettier": "^8.0.0",
+ "@vue/eslint-config-typescript": "^12.0.0",
+ "@vue/tsconfig": "^0.5.1",
+ "eslint": "^8.49.0",
+ "eslint-plugin-vue": "^9.17.0",
+ "npm-run-all2": "^6.1.2",
+ "prettier": "^3.0.3",
+ "typescript": "~5.4.0",
+ "vite": "^5.1.5",
+ "vue-tsc": "^2.0.6"
}
}
diff --git a/pkg/views/public/favicon.svg b/pkg/views/public/favicon.svg
index 3edea9a..c823d24 100755
--- a/pkg/views/public/favicon.svg
+++ b/pkg/views/public/favicon.svg
@@ -1,21 +1,20 @@
-
\ No newline at end of file
+
+
+
diff --git a/pkg/views/src/assets/utils.css b/pkg/views/src/assets/utils.css
new file mode 100644
index 0000000..840e4ee
--- /dev/null
+++ b/pkg/views/src/assets/utils.css
@@ -0,0 +1,15 @@
+html,
+body,
+#app,
+.v-application {
+ overflow: auto !important;
+ font-family: "Roboto Sans", ui-sans-serif, system-ui, sans-serif;
+}
+
+.no-scrollbar {
+ scrollbar-width: none;
+}
+
+.no-scrollbar::-webkit-scrollbar {
+ width: 0;
+}
diff --git a/pkg/views/src/components/AppLoader.tsx b/pkg/views/src/components/AppLoader.tsx
deleted file mode 100644
index b17de46..0000000
--- a/pkg/views/src/components/AppLoader.tsx
+++ /dev/null
@@ -1,14 +0,0 @@
-import { ReactNode, useEffect } from "react";
-import { useWellKnown } from "@/stores/wellKnown.tsx";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-
-export default function AppLoader({ children }: { children: ReactNode }) {
- const { readWellKnown } = useWellKnown();
- const { readProfiles } = useUserinfo();
-
- useEffect(() => {
- Promise.all([readWellKnown(), readProfiles()]);
- }, []);
-
- return children;
-}
\ No newline at end of file
diff --git a/pkg/views/src/components/AppShell.tsx b/pkg/views/src/components/AppShell.tsx
deleted file mode 100644
index 1de91bc..0000000
--- a/pkg/views/src/components/AppShell.tsx
+++ /dev/null
@@ -1,95 +0,0 @@
-import {
- AppBar,
- Avatar,
- Box,
- IconButton,
- Slide,
- Toolbar,
- Typography,
- useMediaQuery,
- useScrollTrigger
-} from "@mui/material";
-import { ReactElement, ReactNode, useEffect, useRef, useState } from "react";
-import { SITE_NAME } from "@/consts";
-import { Link } from "react-router-dom";
-import NavigationMenu, { AppNavigationHeader, isMobileQuery } from "@/components/NavigationMenu.tsx";
-import AccountCircleIcon from "@mui/icons-material/AccountCircleOutlined";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-
-function HideOnScroll(props: { window?: () => Window; children: ReactElement }) {
- const { children, window } = props;
- const trigger = useScrollTrigger({
- target: window ? window() : undefined
- });
-
- return (
-
- {children}
-
- );
-}
-
-export default function AppShell({ children }: { children: ReactNode }) {
- let documentWindow: Window;
-
- const { userinfo } = useUserinfo();
-
- const isMobile = useMediaQuery(isMobileQuery);
- const [open, setOpen] = useState(false);
-
- useEffect(() => {
- documentWindow = window;
- }, []);
-
- const container = useRef(null);
-
- return (
- <>
- documentWindow}>
-
-
-
-
-
-
-
- {SITE_NAME}
-
-
- setOpen(true)}
- sx={{ mr: 1 }}
- >
-
-
-
-
-
-
-
-
-
-
-
- {children}
-
-
- setOpen(false)} />
- >
- );
-}
diff --git a/pkg/views/src/components/Copyright.vue b/pkg/views/src/components/Copyright.vue
new file mode 100644
index 0000000..f3f6baa
--- /dev/null
+++ b/pkg/views/src/components/Copyright.vue
@@ -0,0 +1,6 @@
+
+
+
Copyright © {{ new Date().getFullYear() }} Solsynth
+
Powered by Hydrogen.Identity
+
+
diff --git a/pkg/views/src/components/NavigationMenu.tsx b/pkg/views/src/components/NavigationMenu.tsx
deleted file mode 100644
index b00a387..0000000
--- a/pkg/views/src/components/NavigationMenu.tsx
+++ /dev/null
@@ -1,98 +0,0 @@
-import { Collapse, Divider, ListItemIcon, ListItemText, Menu, MenuItem, styled } from "@mui/material";
-import { theme } from "@/theme";
-import { Fragment, ReactNode, useState } from "react";
-import HowToRegIcon from "@mui/icons-material/HowToReg";
-import LoginIcon from "@mui/icons-material/Login";
-import FaceIcon from "@mui/icons-material/Face";
-import LogoutIcon from "@mui/icons-material/ExitToApp";
-import ExpandLess from "@mui/icons-material/ExpandLess";
-import ExpandMore from "@mui/icons-material/ExpandMore";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import { PopoverProps } from "@mui/material/Popover";
-import { Link } from "react-router-dom";
-
-export interface NavigationItem {
- icon?: ReactNode;
- title?: string;
- link?: string;
- divider?: boolean;
- children?: NavigationItem[];
-}
-
-export const DRAWER_WIDTH = 320;
-
-export const AppNavigationHeader = styled("div")(({ theme }) => ({
- display: "flex",
- alignItems: "center",
- padding: theme.spacing(0, 1),
- justifyContent: "flex-start",
- height: 64,
- ...theme.mixins.toolbar
-}));
-
-export function AppNavigationSection({ items, depth }: { items: NavigationItem[], depth?: number }) {
- const [open, setOpen] = useState(false);
-
- return items.map((item, idx) => {
- if (item.divider) {
- return ;
- } else if (item.children) {
- return (
-
-
-
-
-
-
- );
- } else {
- return (
-
-
-
- );
- }
- });
-}
-
-export function AppNavigation() {
- const { checkLoggedIn } = useUserinfo();
-
- const nav: NavigationItem[] = [
- ...(
- checkLoggedIn() ?
- [
- { icon: , title: "Account", link: "/users" },
- { divider: true },
- { icon: , title: "Sign out", link: "/auth/sign-out" }
- ] :
- [
- { icon: , title: "Sign up", link: "/auth/sign-up" },
- { icon: , title: "Sign in", link: "/auth/sign-in" }
- ]
- )
- ];
-
- return ;
-}
-
-export const isMobileQuery = theme.breakpoints.down("md");
-
-export default function NavigationMenu({ anchorEl, open, onClose }: {
- anchorEl: PopoverProps["anchorEl"];
- open: boolean;
- onClose: () => void
-}) {
- return (
-
- );
-}
diff --git a/pkg/views/src/components/NotificationList.vue b/pkg/views/src/components/NotificationList.vue
new file mode 100644
index 0000000..c5cf0c5
--- /dev/null
+++ b/pkg/views/src/components/NotificationList.vue
@@ -0,0 +1,87 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ You are done! There is no unread notifications for you.
+
+
+
+
+
+ {{ item.subject }}
+ {{ item.content }}
+
+
+
+
+
+
+
+
+
+
+
+ Something went wrong... {{ error }}
+
+
+
diff --git a/pkg/views/src/components/UserMenu.vue b/pkg/views/src/components/UserMenu.vue
new file mode 100644
index 0000000..10a1c28
--- /dev/null
+++ b/pkg/views/src/components/UserMenu.vue
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/components/auth/AccountLocator.vue b/pkg/views/src/components/auth/AccountLocator.vue
new file mode 100644
index 0000000..2f42cec
--- /dev/null
+++ b/pkg/views/src/components/auth/AccountLocator.vue
@@ -0,0 +1,61 @@
+
+
+
+
+
+
+
+ Something went wrong... {{ error }}
+
+
+
+
+ Sign up
+
+
+ Next
+
+
+
+
+
+
+
diff --git a/pkg/views/src/components/auth/CallbackNotify.vue b/pkg/views/src/components/auth/CallbackNotify.vue
new file mode 100644
index 0000000..d63c6d6
--- /dev/null
+++ b/pkg/views/src/components/auth/CallbackNotify.vue
@@ -0,0 +1,16 @@
+
+
+
+
+ You need to sign in before access that page. After you signed in, we will redirect you to:
+ {{ route.query["redirect_uri"] }}
+
+
+
+
+
+
diff --git a/pkg/views/src/components/auth/FactorApplicator.vue b/pkg/views/src/components/auth/FactorApplicator.vue
new file mode 100644
index 0000000..4571c5a
--- /dev/null
+++ b/pkg/views/src/components/auth/FactorApplicator.vue
@@ -0,0 +1,120 @@
+
+
+
+
+
+
+
+
+ Something went wrong... {{ error }}
+
+
+
+
+
+ Next
+
+
+
+
+
+
+
diff --git a/pkg/views/src/components/auth/FactorPicker.vue b/pkg/views/src/components/auth/FactorPicker.vue
new file mode 100644
index 0000000..730a5c5
--- /dev/null
+++ b/pkg/views/src/components/auth/FactorPicker.vue
@@ -0,0 +1,74 @@
+
+
+
+
+
+
+
+
+
+
+
+ Something went wrong... {{ error }}
+
+
+
+
+
+ Next
+
+
+
+
+
+
+
diff --git a/pkg/views/src/consts.tsx b/pkg/views/src/consts.tsx
deleted file mode 100644
index 461c2ca..0000000
--- a/pkg/views/src/consts.tsx
+++ /dev/null
@@ -1 +0,0 @@
-export const SITE_NAME = "Goatpass";
diff --git a/pkg/views/src/error.tsx b/pkg/views/src/error.tsx
deleted file mode 100644
index 8be7cde..0000000
--- a/pkg/views/src/error.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { Link as RouterLink, useRouteError } from "react-router-dom";
-import { Box, Container, Link, Typography } from "@mui/material";
-
-export default function ErrorBoundary() {
- const error = useRouteError() as any;
-
- return (
-
-
- {error.status}
- {error?.message ?? "Something went wrong"}
-
- Back to homepage
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/index.css b/pkg/views/src/index.css
deleted file mode 100644
index e69de29..0000000
diff --git a/pkg/views/src/index.vue b/pkg/views/src/index.vue
new file mode 100644
index 0000000..4f21c35
--- /dev/null
+++ b/pkg/views/src/index.vue
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pkg/views/src/layouts/master.vue b/pkg/views/src/layouts/master.vue
new file mode 100644
index 0000000..ebbe1a8
--- /dev/null
+++ b/pkg/views/src/layouts/master.vue
@@ -0,0 +1,46 @@
+
+
+
+
+
+ Solarpass
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/layouts/user-center.vue b/pkg/views/src/layouts/user-center.vue
new file mode 100644
index 0000000..437106f
--- /dev/null
+++ b/pkg/views/src/layouts/user-center.vue
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/main.ts b/pkg/views/src/main.ts
new file mode 100644
index 0000000..b3665d3
--- /dev/null
+++ b/pkg/views/src/main.ts
@@ -0,0 +1,54 @@
+import "virtual:uno.css"
+
+import "./assets/utils.css"
+
+import { createApp } from "vue"
+import { createPinia } from "pinia"
+
+import "vuetify/styles"
+import { createVuetify } from "vuetify"
+import { md3 } from "vuetify/blueprints"
+import * as components from "vuetify/components"
+import * as labsComponents from "vuetify/labs/components"
+import * as directives from "vuetify/directives"
+
+import "@mdi/font/css/materialdesignicons.min.css"
+import "@fontsource/roboto/latin.css"
+import "@unocss/reset/tailwind.css"
+
+import index from "./index.vue"
+import router from "./router"
+
+const app = createApp(index)
+
+app.use(
+ createVuetify({
+ directives,
+ components: {
+ ...components,
+ ...labsComponents,
+ },
+ blueprint: md3,
+ theme: {
+ defaultTheme: "original",
+ themes: {
+ original: {
+ colors: {
+ primary: "#4a5099",
+ secondary: "#2196f3",
+ accent: "#009688",
+ error: "#f44336",
+ warning: "#ff9800",
+ info: "#03a9f4",
+ success: "#4caf50",
+ },
+ },
+ },
+ },
+ }),
+)
+
+app.use(createPinia())
+app.use(router)
+
+app.mount("#app")
diff --git a/pkg/views/src/main.tsx b/pkg/views/src/main.tsx
deleted file mode 100644
index 4d8c499..0000000
--- a/pkg/views/src/main.tsx
+++ /dev/null
@@ -1,90 +0,0 @@
-import React from "react";
-import ReactDOM from "react-dom/client";
-import { createBrowserRouter, Outlet, RouterProvider } from "react-router-dom";
-import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
-import { LocalizationProvider } from "@mui/x-date-pickers";
-import { CssBaseline, ThemeProvider } from "@mui/material";
-import { theme } from "@/theme.ts";
-
-import "virtual:uno.css";
-
-import "./index.css";
-import "@unocss/reset/tailwind.css";
-import "@fontsource/roboto/latin.css";
-
-import AppShell from "@/components/AppShell.tsx";
-import ErrorBoundary from "@/error.tsx";
-import AppLoader from "@/components/AppLoader.tsx";
-import UserLayout from "@/pages/users/layout.tsx";
-import { UserinfoProvider } from "@/stores/userinfo.tsx";
-import { WellKnownProvider } from "@/stores/wellKnown.tsx";
-import AuthLayout from "@/pages/auth/layout.tsx";
-import AuthGuard from "@/pages/guard.tsx";
-
-declare const __GARFISH_EXPORTS__: {
- provider: Object;
- registerProvider?: (provider: any) => void;
-};
-
-declare global {
- interface Window {
- __LAUNCHPAD_TARGET__?: string;
- }
-}
-
-const router = createBrowserRouter([
- {
- path: "/",
- element: ,
- errorElement: ,
- children: [
- { path: "/", lazy: () => import("@/pages/landing.tsx") },
- {
- path: "/",
- element: ,
- children: [
- {
- path: "/users",
- element: ,
- children: [
- { path: "/users", lazy: () => import("@/pages/users/dashboard.tsx") },
- { path: "/users/notifications", lazy: () => import("@/pages/users/notifications.tsx") },
- { path: "/users/personalize", lazy: () => import("@/pages/users/personalize.tsx") },
- { path: "/users/security", lazy: () => import("@/pages/users/security.tsx") }
- ]
- }
- ]
- }
- ]
- },
- {
- path: "/auth",
- element: ,
- errorElement: ,
- children: [
- { path: "/auth/sign-up", errorElement: , lazy: () => import("@/pages/auth/sign-up.tsx") },
- { path: "/auth/sign-in", errorElement: , lazy: () => import("@/pages/auth/sign-in.tsx") },
- { path: "/auth/sign-out", errorElement: , lazy: () => import("@/pages/auth/sign-out.tsx") },
- { path: "/auth/o/connect", errorElement: , lazy: () => import("@/pages/auth/connect.tsx") }
- ]
- }
-]);
-
-const element = (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-);
-
-ReactDOM.createRoot(document.getElementById("root")!).render(element);
\ No newline at end of file
diff --git a/pkg/views/src/pages/auth/connect.tsx b/pkg/views/src/pages/auth/connect.tsx
deleted file mode 100644
index ce69c4e..0000000
--- a/pkg/views/src/pages/auth/connect.tsx
+++ /dev/null
@@ -1,182 +0,0 @@
-import { useEffect, useState } from "react";
-import {
- Alert,
- Avatar,
- Box,
- Button,
- Card,
- CardContent,
- Collapse,
- Grid,
- LinearProgress,
- Typography
-} from "@mui/material";
-import { request } from "@/scripts/request.ts";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import { useSearchParams } from "react-router-dom";
-import OutletIcon from "@mui/icons-material/Outlet";
-import WhatshotIcon from "@mui/icons-material/Whatshot";
-
-export function Component() {
- const { getAtk } = useUserinfo();
-
- const [panel, setPanel] = useState(0);
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(false);
-
- const [client, setClient] = useState(null);
-
- const [searchParams] = useSearchParams();
-
- async function preconnect() {
- const res = await request(`/api/auth/o/connect${location.search}`, {
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
-
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
-
- if (data["session"]) {
- setPanel(1);
- redirect(data["session"]);
- } else {
- setClient(data["client"]);
- setLoading(false);
- }
- }
- }
-
- useEffect(() => {
- preconnect().then(() => console.log("Fetched metadata"));
- }, []);
-
- function decline() {
- if (window.history.length > 0) {
- window.history.back();
- } else {
- window.close();
- }
- }
-
- async function approve() {
- setLoading(true);
-
- const res = await request("/api/auth/o/connect?" + new URLSearchParams({
- client_id: searchParams.get("client_id") as string,
- redirect_uri: encodeURIComponent(searchParams.get("redirect_uri") as string),
- response_type: "code",
- scope: searchParams.get("scope") as string
- }), {
- method: "POST",
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
-
- if (res.status !== 200) {
- setError(await res.text());
- setLoading(false);
- } else {
- const data = await res.json();
- setPanel(1);
- setTimeout(() => redirect(data["session"]), 1850);
- }
- }
-
- function redirect(session: any) {
- const url = `${searchParams.get("redirect_uri")}?code=${session["grant_token"]}&state=${searchParams.get("state")}`;
- window.open(url, "_self");
- }
-
- const elements = [
- (
- <>
-
-
-
-
- Sign in to {client?.name}
-
-
-
-
- About this app
- {client?.description}
-
-
- Make you trust this app
-
- After you click Approve button, you will share your basic personal information to this application
- developer. Some of them will leak your data. Think twice.
-
-
-
-
-
-
-
-
-
-
- >
- ),
- (
- <>
-
-
-
-
- Authorized
-
-
-
-
- Now Redirecting...
- Hold on a second, we are going to redirect you to the target.
-
-
-
- >
- )
- ];
-
- return (
- <>
- {error && {error}}
-
-
-
-
-
-
-
- {elements[panel]}
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/auth/layout.tsx b/pkg/views/src/pages/auth/layout.tsx
deleted file mode 100644
index f9dec4c..0000000
--- a/pkg/views/src/pages/auth/layout.tsx
+++ /dev/null
@@ -1,12 +0,0 @@
-import { Box } from "@mui/material";
-import { Outlet } from "react-router-dom";
-
-export default function AuthLayout() {
- return (
-
-
-
-
-
- )
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/auth/sign-in.tsx b/pkg/views/src/pages/auth/sign-in.tsx
deleted file mode 100644
index 6c43587..0000000
--- a/pkg/views/src/pages/auth/sign-in.tsx
+++ /dev/null
@@ -1,331 +0,0 @@
-import { Link as RouterLink, useNavigate, useSearchParams } from "react-router-dom";
-import {
- Alert,
- Avatar,
- Box,
- Button,
- Card,
- CardContent,
- Collapse,
- Grid,
- LinearProgress,
- Link,
- Paper,
- TextField,
- ToggleButton,
- ToggleButtonGroup,
- Typography
-} from "@mui/material";
-import { FormEvent, useState } from "react";
-import { request } from "@/scripts/request.ts";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import LoginIcon from "@mui/icons-material/Login";
-import SecurityIcon from "@mui/icons-material/Security";
-import KeyIcon from "@mui/icons-material/Key";
-import PasswordIcon from "@mui/icons-material/Password";
-import EmailIcon from "@mui/icons-material/Email";
-
-export function Component() {
- const [panel, setPanel] = useState(0);
-
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(false);
-
- const [factor, setFactor] = useState();
- const [factorType, setFactorType] = useState();
-
- const [factors, setFactors] = useState(null);
- const [challenge, setChallenge] = useState(null);
-
- const { readProfiles } = useUserinfo();
-
- const [searchParams] = useSearchParams();
- const navigate = useNavigate();
-
- const handlers: any[] = [
- async (evt: FormEvent) => {
- evt.preventDefault();
-
- const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
- if (!data.id) return;
-
- setLoading(true);
- const res = await request("/api/auth", {
- method: "PUT",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(data)
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- setFactors(data["factors"]);
- setChallenge(data["challenge"]);
- setPanel(1);
- setError(null);
- }
- setLoading(false);
- },
- async (evt: FormEvent) => {
- evt.preventDefault();
-
- if (!factor) return;
-
- setLoading(true);
- const res = await request(`/api/auth/factors/${factor}`, {
- method: "POST"
- });
- if (res.status !== 200 && res.status !== 204) {
- setError(await res.text());
- } else {
- const item = factors.find((item: any) => item.id === factor).type;
- setError(null);
- setPanel(2);
- setFactorType(factorTypes[item]);
- }
- setLoading(false);
- },
- async (evt: SubmitEvent) => {
- evt.preventDefault();
-
- const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
- if (!data.credentials) return;
-
- setLoading(true);
- const res = await request(`/api/auth`, {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- challenge_id: challenge?.id,
- factor_id: factor,
- secret: data.credentials
- })
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- if (data["is_finished"]) {
- await grantToken(data["session"]["grant_token"]);
- await readProfiles();
- callback();
- } else {
- setError(null);
- setPanel(1);
- setFactor(undefined);
- setFactorType(undefined);
- setChallenge(data["challenge"]);
- }
- }
- setLoading(false);
- }
- ];
-
- function callback() {
- if (searchParams.has("closable")) {
- window.close();
- } else if (searchParams.has("redirect_uri")) {
- window.open(searchParams.get("redirect_uri") ?? "/", "_self");
- } else {
- navigate("/users");
- }
- }
-
- function getFactorAvailable(factor: any) {
- const blacklist: number[] = challenge?.blacklist_factors ?? [];
- return blacklist.includes(factor.id);
- }
-
- const factorTypes = [
- { icon: , label: "Password Verification", autoComplete: "password" },
- { icon: , label: "Email One Time Password", autoComplete: "one-time-code" }
- ];
-
- const elements = [
- (
- <>
-
-
-
-
- Welcome back
-
-
-
-
-
-
-
-
-
- >
- ),
- (
- <>
-
-
-
-
- Verify that's you
-
-
-
-
- setFactor(val)}
- >
- {factors?.map((item: any, idx: number) => (
-
-
-
- {factorTypes[item.type]?.icon}
-
-
- {factorTypes[item.type]?.label}
-
-
-
- ))}
-
-
-
-
-
- >
- ),
- (
- <>
-
-
-
-
- Enter the credentials
-
-
-
-
-
-
-
-
-
- >
- )
- ];
-
- async function grantToken(tk: string) {
- const res = await request("/api/auth/token", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify({
- code: tk,
- grant_type: "grant_token"
- })
- });
- if (res.status !== 200) {
- const err = await res.text();
- setError(err);
- throw new Error(err);
- } else {
- setError(null);
- }
- }
-
- return (
- <>
- {error && {error}}
-
-
-
- You need sign in before take an action. After that, we will take you back to your work.
-
-
-
-
-
-
-
-
-
- {elements[panel]}
-
-
-
-
-
-
- Risk {challenge?.risk_level}
- Progress {challenge?.progress}/{challenge?.requirements}
-
-
-
-
-
-
-
-
-
-
- Haven't an account? Sign up!
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/auth/sign-out.tsx b/pkg/views/src/pages/auth/sign-out.tsx
deleted file mode 100644
index 6c37a8d..0000000
--- a/pkg/views/src/pages/auth/sign-out.tsx
+++ /dev/null
@@ -1,50 +0,0 @@
-import { Avatar, Button, Card, CardContent, Typography } from "@mui/material";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import LogoutIcon from "@mui/icons-material/Logout";
-import { useNavigate } from "react-router-dom";
-
-export function Component() {
- const { clearUserinfo } = useUserinfo();
-
- const navigate = useNavigate();
-
- async function signout() {
- clearUserinfo();
- navigate("/");
- }
-
- return (
- <>
-
-
-
-
-
-
- Sign out
-
- Sign out will clear your data on this device. Also will affected those use union identification services.
- You need sign in again get access them.
-
-
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/auth/sign-up.tsx b/pkg/views/src/pages/auth/sign-up.tsx
deleted file mode 100644
index b57f5d1..0000000
--- a/pkg/views/src/pages/auth/sign-up.tsx
+++ /dev/null
@@ -1,198 +0,0 @@
-import UserIcon from "@mui/icons-material/PersonAddAlt1";
-import HowToRegIcon from "@mui/icons-material/HowToReg";
-import { Link as RouterLink, useNavigate, useSearchParams } from "react-router-dom";
-import {
- Alert,
- Avatar,
- Box,
- Button,
- Card,
- CardContent,
- Checkbox,
- Collapse,
- FormControlLabel,
- Grid,
- LinearProgress,
- Link,
- TextField,
- Typography
-} from "@mui/material";
-import { FormEvent, useState } from "react";
-import { request } from "@/scripts/request.ts";
-import { useWellKnown } from "@/stores/wellKnown.tsx";
-
-export function Component() {
- const [done, setDone] = useState(false);
-
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(false);
-
- const { wellKnown } = useWellKnown();
-
- const [searchParams] = useSearchParams();
- const navigate = useNavigate();
-
- async function submit(evt: FormEvent) {
- evt.preventDefault();
-
- const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
- if (!data.human_verification) return;
- if (!data.name || !data.nick || !data.email || !data.password) return;
-
- setLoading(true);
- const res = await request("/api/users", {
- method: "POST",
- headers: { "Content-Type": "application/json" },
- body: JSON.stringify(data)
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- setError(null);
- setDone(true);
- }
- setLoading(false);
- }
-
- function callback() {
- if (searchParams.has("closable")) {
- window.close();
- } else {
- navigate("/auth/sign-in");
- }
- }
-
- const elements = [
- (
- <>
-
-
-
-
- Create an account
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {
- !wellKnown?.open_registration &&
-
-
- }
-
- }
- label={"I'm not a robot."}
- />
-
-
-
-
- >
- ),
- (
- <>
-
-
-
-
- Congratulations!
-
- Your account has been created and activation email has sent to your inbox!
-
-
-
- callback()} className="cursor-pointer">Go login
-
-
-
- After you login, then you can take part in the entire smartsheep community.
-
- >
- )
- ];
-
- return (
- <>
- {error && {error}}
-
-
-
-
-
-
-
- {!done ? elements[0] : elements[1]}
-
-
-
-
-
-
- Already have an account? Sign in!
-
-
-
- >
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/guard.tsx b/pkg/views/src/pages/guard.tsx
deleted file mode 100644
index ce39a4c..0000000
--- a/pkg/views/src/pages/guard.tsx
+++ /dev/null
@@ -1,29 +0,0 @@
-import { useEffect } from "react";
-import { Box, CircularProgress } from "@mui/material";
-import { Outlet, useLocation, useNavigate } from "react-router-dom";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-
-export default function AuthGuard() {
- const { userinfo } = useUserinfo();
-
- const navigate = useNavigate();
- const location = useLocation();
-
- useEffect(() => {
- console.log(userinfo)
- if (userinfo?.isReady) {
- if (!userinfo?.isLoggedIn) {
- const callback = location.pathname + location.search;
- navigate({ pathname: "/auth/sign-in", search: `redirect_uri=${callback}` });
- }
- }
- }, [userinfo]);
-
- return !userinfo?.isReady ? (
-
-
-
-
-
- ) : ;
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/landing.tsx b/pkg/views/src/pages/landing.tsx
deleted file mode 100644
index 1d4d998..0000000
--- a/pkg/views/src/pages/landing.tsx
+++ /dev/null
@@ -1,22 +0,0 @@
-import { Button, Container, Grid, Typography } from "@mui/material";
-import { Link as RouterLink } from "react-router-dom";
-
-export function Component() {
- return (
-
-
-
- All Goatworks® Services
- In a single account
-
- That's
- Goatpass
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/users/dashboard.tsx b/pkg/views/src/pages/users/dashboard.tsx
deleted file mode 100644
index 6ab9ba7..0000000
--- a/pkg/views/src/pages/users/dashboard.tsx
+++ /dev/null
@@ -1,35 +0,0 @@
-import { Alert, Box, Card, CardContent, Container, Typography } from "@mui/material";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-
-export function Component() {
- const { userinfo } = useUserinfo();
-
- return (
-
-
- Welcome, {userinfo?.displayName}
- What can I help you today?
-
-
- {
- !userinfo?.data?.confirmed_at &&
-
- Your account haven't confirmed yet. Go to your linked email
- inbox and check out our registration confirm email.
-
- }
-
-
- Frequently Asked Questions
-
-
-
- 没有人有问题。没有人敢有问题。鲁迅曾经说过:
- 解决不了问题,就解决提问题的人。 —— 鲁迅
- 所以,我们的客诉率是 0% 哦~
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/users/layout.tsx b/pkg/views/src/pages/users/layout.tsx
deleted file mode 100644
index df6147c..0000000
--- a/pkg/views/src/pages/users/layout.tsx
+++ /dev/null
@@ -1,65 +0,0 @@
-import { Outlet, useLocation, useNavigate } from "react-router-dom";
-import { Box, Tab, Tabs, useMediaQuery } from "@mui/material";
-import { useEffect, useState } from "react";
-import { theme } from "@/theme.ts";
-import DashboardIcon from "@mui/icons-material/Dashboard";
-import InboxIcon from "@mui/icons-material/Inbox";
-import DrawIcon from "@mui/icons-material/Draw";
-import SecurityIcon from "@mui/icons-material/Security";
-
-export default function UserLayout() {
- const [focus, setFocus] = useState(0);
-
- const isMobile = useMediaQuery(theme.breakpoints.down("md"));
-
- const locations = ["/users", "/users/notifications", "/users/personalize", "/users/security"];
- const tabs = [
- { icon: , label: "Dashboard" },
- { icon: , label: "Notifications" },
- { icon: , label: "Personalize" },
- { icon: , label: "Security" }
- ];
-
- const location = useLocation();
- const navigate = useNavigate();
-
- useEffect(() => {
- const idx = locations.indexOf(location.pathname);
- setFocus(idx);
- }, []);
-
- function swap(idx: number) {
- navigate(locations[idx]);
- setFocus(idx);
- }
-
- return (
-
-
- swap(val)}
- sx={{
- borderRight: isMobile ? 0 : 1,
- borderBottom: isMobile ? 1 : 0,
- borderColor: "divider",
- height: isMobile ? "fit-content" : "100%",
- py: isMobile ? 0 : 1,
- px: isMobile ? 1 : 0
- }}
- >
- {tabs.map((tab, idx) => (
-
- ))}
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/users/notifications.tsx b/pkg/views/src/pages/users/notifications.tsx
deleted file mode 100644
index 95814da..0000000
--- a/pkg/views/src/pages/users/notifications.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { Alert, Box, Collapse, IconButton, LinearProgress, List, ListItem, ListItemText } from "@mui/material";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import { request } from "@/scripts/request.ts";
-import { useEffect, useState } from "react";
-import { TransitionGroup } from "react-transition-group";
-import MarkEmailReadIcon from "@mui/icons-material/MarkEmailRead";
-
-export function Component() {
- const { userinfo, readProfiles, getAtk } = useUserinfo();
-
- const [loading, setLoading] = useState(true);
- const [error, setError] = useState(null);
-
- const [notifications, setNotifications] = useState([]);
-
- async function readNotifications() {
- const res = await request(`/api/notifications?take=100`, {
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- setNotifications(data["data"]);
- setError(null);
- }
- }
-
- async function markNotifications(item: any) {
- setLoading(true);
- const res = await request(`/api/notifications/${item.id}/read`, {
- method: "PUT",
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- readNotifications().then(() => readProfiles());
- setError(null);
- }
- setLoading(false);
- }
-
- useEffect(() => {
- readNotifications().then(() => setLoading(false));
- }, []);
-
- return (
-
-
-
-
-
-
- {error}
-
-
-
- You are done! There's no unread notifications for you.
-
-
-
-
- {notifications.map((item, idx) => (
-
- markNotifications(item)}
- >
-
-
- }>
-
-
-
- ))}
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/users/personalize.tsx b/pkg/views/src/pages/users/personalize.tsx
deleted file mode 100644
index fa9f02a..0000000
--- a/pkg/views/src/pages/users/personalize.tsx
+++ /dev/null
@@ -1,250 +0,0 @@
-import {
- Alert,
- Avatar,
- Box,
- Button,
- Card,
- CardContent,
- CircularProgress,
- Collapse,
- Container,
- Divider,
- Grid,
- LinearProgress,
- Snackbar,
- styled,
- TextField,
- Typography
-} from "@mui/material";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import { ChangeEvent, FormEvent, useState } from "react";
-import { DatePicker } from "@mui/x-date-pickers";
-import { request } from "@/scripts/request.ts";
-import SaveIcon from "@mui/icons-material/Save";
-import PublishIcon from "@mui/icons-material/Publish";
-import NoAccountsIcon from "@mui/icons-material/NoAccounts";
-import dayjs from "dayjs";
-
-const VisuallyHiddenInput = styled("input")({
- clip: "rect(0 0 0 0)",
- clipPath: "inset(50%)",
- height: 1,
- overflow: "hidden",
- position: "absolute",
- bottom: 0,
- left: 0,
- whiteSpace: "nowrap",
- width: 1
-});
-
-export function Component() {
- const { userinfo, readProfiles, getAtk } = useUserinfo();
-
- const [done, setDone] = useState(false);
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(false);
-
- async function submit(evt: FormEvent) {
- evt.preventDefault();
-
- const data: any = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
- if (data.birthday) data.birthday = new Date(data.birthday);
-
- setLoading(true);
- const res = await request("/api/users/me", {
- method: "PUT",
- headers: { "Content-Type": "application/json", "Authorization": `Bearer ${getAtk()}` },
- body: JSON.stringify(data)
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readProfiles();
- setDone(true);
- setError(null);
- }
- setLoading(false);
- }
-
- async function changeAvatar(evt: ChangeEvent) {
- if (!evt.target.files) return;
-
- const file = evt.target.files[0];
- const payload = new FormData();
- payload.set("avatar", file);
-
- setLoading(true);
- const res = await request("/api/avatar", {
- method: "PUT",
- headers: { "Authorization": `Bearer ${getAtk()}` },
- body: payload
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readProfiles();
- setDone(true);
- setError(null);
- }
- setLoading(false);
- }
-
- function getBirthday() {
- return userinfo?.data?.profile?.birthday ? dayjs(userinfo?.data?.profile?.birthday) : undefined;
- }
-
- const basisForm = (
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- }
- sx={{ mt: 2, width: "180px" }}
- >
- Save changes
-
-
-
-
-
-
-
-
-
-
-
- {/* @ts-ignore */}
- }
- sx={{ width: "180px" }}
- >
- Change avatar
-
-
-
-
-
- );
-
- return (
-
-
- Personalize
-
- Customize your appearance and name card across all Goatworks information.
-
-
-
-
- {error}
-
-
-
-
-
-
-
-
-
-
- Information
-
- The information for public. Let us and others better to know who you are.
-
-
-
- {
- userinfo?.data != null ? basisForm :
-
-
-
- }
-
-
-
-
-
- setDone(false)}
- message="Your profile has been updated. Some settings maybe need sometime to apply across site."
- />
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/pages/users/security.tsx b/pkg/views/src/pages/users/security.tsx
deleted file mode 100644
index f978590..0000000
--- a/pkg/views/src/pages/users/security.tsx
+++ /dev/null
@@ -1,267 +0,0 @@
-import {
- Alert,
- Box,
- Card,
- CardContent,
- Collapse,
- Container,
- Grid,
- LinearProgress,
- Tab,
- Tabs,
- Typography
-} from "@mui/material";
-import { useUserinfo } from "@/stores/userinfo.tsx";
-import { TabContext, TabPanel } from "@mui/lab";
-import { useEffect, useState } from "react";
-import { DataGrid, GridActionsCellItem, GridColDef, GridRowParams, GridValueGetterParams } from "@mui/x-data-grid";
-import { request } from "@/scripts/request.ts";
-import ExitToAppIcon from "@mui/icons-material/ExitToApp";
-
-
-export function Component() {
- const dataDefinitions: { [id: string]: GridColDef[] } = {
- challenges: [
- { field: "id", headerName: "ID", width: 64 },
- { field: "ip_address", headerName: "IP Address", minWidth: 128 },
- { field: "user_agent", headerName: "User Agent", minWidth: 320 },
- {
- field: "created_at",
- headerName: "Issued At",
- minWidth: 160,
- valueGetter: (params: GridValueGetterParams) => new Date(params.row.created_at).toLocaleString()
- }
- ],
- sessions: [
- { field: "id", headerName: "ID", width: 64 },
- {
- field: "audiences",
- headerName: "Audiences",
- minWidth: 128,
- valueGetter: (params: GridValueGetterParams) => params.row.audiences.join(", ")
- },
- {
- field: "claims",
- headerName: "Claims",
- minWidth: 224,
- valueGetter: (params: GridValueGetterParams) => params.row.claims.join(", ")
- },
- {
- field: "created_at",
- headerName: "Issued At",
- minWidth: 160,
- valueGetter: (params: GridValueGetterParams) => new Date(params.row.created_at).toLocaleString()
- },
- {
- field: "actions",
- type: "actions",
- getActions: (params: GridRowParams) => [
- }
- onClick={() => killSession(params.row)}
- disabled={loading}
- label="Sign Out"
- />
- ]
- }
- ],
- events: [
- { field: "id", headerName: "ID", width: 64 },
- { field: "type", headerName: "Type", minWidth: 128 },
- { field: "target", headerName: "Affected Object", minWidth: 128 },
- { field: "ip_address", headerName: "IP Address", minWidth: 128 },
- { field: "user_agent", headerName: "User Agent", minWidth: 128 },
- {
- field: "created_at",
- headerName: "Performed At",
- minWidth: 160,
- valueGetter: (params: GridValueGetterParams) => new Date(params.row.created_at).toLocaleString()
- }
- ]
- };
-
- const { getAtk } = useUserinfo();
-
- const [challenges, setChallenges] = useState([]);
- const [challengeCount, setChallengeCount] = useState(0);
- const [sessions, setSessions] = useState([]);
- const [sessionCount, setSessionCount] = useState(0);
- const [events, setEvents] = useState([]);
- const [eventCount, setEventCount] = useState(0);
-
- const [pagination, setPagination] = useState({
- challenges: { page: 0, pageSize: 5 },
- sessions: { page: 0, pageSize: 5 },
- events: { page: 0, pageSize: 5 }
- });
-
- const [error, setError] = useState(null);
- const [loading, setLoading] = useState(false);
- const [reverting] = useState({
- challenges: true,
- sessions: true,
- events: true
- });
-
- const [dataPane, setDataPane] = useState("challenges");
-
- async function readChallenges() {
- reverting.challenges = true;
- const res = await request("/api/users/me/challenges?" + new URLSearchParams({
- take: pagination.challenges.pageSize.toString(),
- offset: (pagination.challenges.page * pagination.challenges.pageSize).toString()
- }), {
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- setChallenges(data["data"]);
- setChallengeCount(data["count"]);
- }
- reverting.challenges = false;
- }
-
- async function readSessions() {
- reverting.sessions = true;
- const res = await request("/api/users/me/sessions?" + new URLSearchParams({
- take: pagination.sessions.pageSize.toString(),
- offset: (pagination.sessions.page * pagination.sessions.pageSize).toString()
- }), {
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- setSessions(data["data"]);
- setSessionCount(data["count"]);
- }
- reverting.sessions = false;
- }
-
- async function readEvents() {
- reverting.events = true;
- const res = await request("/api/users/me/events?" + new URLSearchParams({
- take: pagination.events.pageSize.toString(),
- offset: (pagination.events.page * pagination.events.pageSize).toString()
- }), {
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- const data = await res.json();
- setEvents(data["data"]);
- setEventCount(data["count"]);
- }
- reverting.events = false;
- }
-
- async function killSession(item: any) {
- setLoading(true);
- const res = await request(`/api/users/me/sessions/${item.id}`, {
- method: "DELETE",
- headers: { Authorization: `Bearer ${getAtk()}` }
- });
- if (res.status !== 200) {
- setError(await res.text());
- } else {
- await readSessions();
- setError(null);
- }
- setLoading(false);
- }
-
- useEffect(() => {
- readChallenges().then(() => console.log("Refreshed challenges list."));
- }, [pagination.challenges]);
-
- useEffect(() => {
- readSessions().then(() => console.log("Refreshed sessions list."));
- }, [pagination.sessions]);
-
- useEffect(() => {
- readEvents().then(() => console.log("Refreshed events list."));
- }, [pagination.events]);
-
- return (
-
-
- Security
-
- Overview and control all security details in your account.
-
-
-
-
- {error}
-
-
-
-
-
-
-
-
-
-
-
-
- setDataPane(val)}>
-
-
-
-
-
-
-
-
- setPagination({ ...pagination, challenges: val })}
- checkboxSelection
- />
-
-
- setPagination({ ...pagination, sessions: val })}
- checkboxSelection
- />
-
-
- setPagination({ ...pagination, events: val })}
- checkboxSelection
- />
-
-
-
-
-
-
-
-
- );
-}
\ No newline at end of file
diff --git a/pkg/views/src/router/index.ts b/pkg/views/src/router/index.ts
new file mode 100644
index 0000000..c0981ee
--- /dev/null
+++ b/pkg/views/src/router/index.ts
@@ -0,0 +1,83 @@
+import { createRouter, createWebHistory } from "vue-router"
+import { useUserinfo } from "@/stores/userinfo"
+import MasterLayout from "@/layouts/master.vue"
+import UserCenterLayout from "@/layouts/user-center.vue"
+
+const router = createRouter({
+ history: createWebHistory(import.meta.env.BASE_URL),
+ routes: [
+ {
+ path: "/",
+ component: MasterLayout,
+ children: [
+ {
+ path: "/",
+ component: UserCenterLayout,
+ children: [
+ {
+ path: "/",
+ name: "dashboard",
+ component: () => import("@/views/dashboard.vue"),
+ meta: { title: "Your account" },
+ },
+ {
+ path: "/me/personalize",
+ name: "personalize",
+ component: () => import("@/views/personalize.vue"),
+ meta: { title: "Your personality" },
+ },
+ {
+ path: "/me/security",
+ name: "security",
+ component: () => import("@/views/security.vue"),
+ meta: { title: "Your security" },
+ },
+ ],
+ },
+ ],
+ },
+ {
+ path: "/auth",
+ children: [
+ {
+ path: "sign-in",
+ name: "auth.sign-in",
+ component: () => import("@/views/auth/sign-in.vue"),
+ meta: { public: true, title: "Sign in" },
+ },
+ {
+ path: "sign-up",
+ name: "auth.sign-up",
+ component: () => import("@/views/auth/sign-up.vue"),
+ meta: { public: true, title: "Sign up" },
+ },
+ {
+ path: "o/connect",
+ name: "openid.connect",
+ component: () => import("@/views/auth/connect.vue"),
+ },
+ ],
+ },
+ ],
+})
+
+router.beforeEach(async (to, from, next) => {
+ const id = useUserinfo()
+ if (!id.isReady) {
+ await id.readProfiles()
+ }
+
+ if (to.meta.title) {
+ document.title = `Solarpass | ${to.meta.title}`
+ } else {
+ document.title = "Solarpass"
+ }
+
+ if (!to.meta.public && !id.userinfo.isLoggedIn) {
+ next({ name: "auth.sign-in", query: { redirect_uri: to.fullPath } })
+ } else {
+ next()
+ }
+})
+
+export default router
diff --git a/pkg/views/src/scripts/request.ts b/pkg/views/src/scripts/request.ts
index b85771b..5540ff2 100644
--- a/pkg/views/src/scripts/request.ts
+++ b/pkg/views/src/scripts/request.ts
@@ -1,4 +1,10 @@
+declare global {
+ interface Window {
+ __LAUNCHPAD_TARGET__?: string
+ }
+}
+
export async function request(input: string, init?: RequestInit) {
- const prefix = window.__LAUNCHPAD_TARGET__ ?? "";
+ const prefix = window.__LAUNCHPAD_TARGET__ ?? ""
return await fetch(prefix + input, init)
-}
\ No newline at end of file
+}
diff --git a/pkg/views/src/stores/userinfo.ts b/pkg/views/src/stores/userinfo.ts
new file mode 100644
index 0000000..8636dc7
--- /dev/null
+++ b/pkg/views/src/stores/userinfo.ts
@@ -0,0 +1,54 @@
+import Cookie from "universal-cookie"
+import { defineStore } from "pinia"
+import { ref } from "vue"
+import { request } from "@/scripts/request"
+
+export interface Userinfo {
+ isLoggedIn: boolean
+ displayName: string
+ data: any
+}
+
+const defaultUserinfo: Userinfo = {
+ isLoggedIn: false,
+ displayName: "Citizen",
+ data: null,
+}
+
+export function getAtk(): string {
+ return new Cookie().get("identity_auth_key")
+}
+
+export function checkLoggedIn(): boolean {
+ return new Cookie().get("identity_auth_key")
+}
+
+export const useUserinfo = defineStore("userinfo", () => {
+ const userinfo = ref(defaultUserinfo)
+ const isReady = ref(false)
+
+ async function readProfiles() {
+ if (!checkLoggedIn()) {
+ isReady.value = true
+ }
+
+ const res = await request("/api/users/me", {
+ headers: { Authorization: `Bearer ${getAtk()}` },
+ })
+
+ if (res.status !== 200) {
+ return
+ }
+
+ const data = await res.json()
+
+ isReady.value = true
+ userinfo.value = {
+ isLoggedIn: true,
+ displayName: data["nick"],
+ data: data,
+ }
+ }
+
+ return { userinfo, isReady, readProfiles }
+})
diff --git a/pkg/views/src/stores/userinfo.tsx b/pkg/views/src/stores/userinfo.tsx
deleted file mode 100644
index 4b7aa5c..0000000
--- a/pkg/views/src/stores/userinfo.tsx
+++ /dev/null
@@ -1,79 +0,0 @@
-import Cookie from "universal-cookie";
-import { request } from "../scripts/request.ts";
-import { createContext, useContext, useState } from "react";
-
-export interface Userinfo {
- isReady: boolean,
- isLoggedIn: boolean,
- displayName: string,
- data: any,
-}
-
-const defaultUserinfo: Userinfo = {
- isReady: false,
- isLoggedIn: false,
- displayName: "Citizen",
- data: null
-};
-
-const UserinfoContext = createContext({ userinfo: defaultUserinfo });
-
-export function UserinfoProvider(props: any) {
- const [userinfo, setUserinfo] = useState(structuredClone(defaultUserinfo));
-
- function getAtk(): string {
- return new Cookie().get("identity_auth_key");
- }
-
- function checkLoggedIn(): boolean {
- return new Cookie().get("identity_auth_key");
- }
-
- async function readProfiles() {
- if (!checkLoggedIn()) {
- setUserinfo((data) => {
- data.isReady = true;
- return data;
- });
- }
-
- const res = await request("/api/users/me", {
- headers: { "Authorization": `Bearer ${getAtk()}` }
- });
-
- if (res.status !== 200) {
- return;
- }
-
- const data = await res.json();
-
- setUserinfo({
- isReady: true,
- isLoggedIn: true,
- displayName: data["nick"],
- data: data
- });
- }
-
- function clearUserinfo() {
- const cookies = document.cookie.split(";");
- for (let i = 0; i < cookies.length; i++) {
- const cookie = cookies[i];
- const eqPos = cookie.indexOf("=");
- const name = eqPos > -1 ? cookie.substring(0, eqPos) : cookie;
- document.cookie = name + "=;expires=Thu, 01 Jan 1970 00:00:00 GMT";
- }
-
- setUserinfo(defaultUserinfo);
- }
-
- return (
-
- {props.children}
-
- );
-}
-
-export function useUserinfo() {
- return useContext(UserinfoContext);
-}
\ No newline at end of file
diff --git a/pkg/views/src/stores/wellKnown.tsx b/pkg/views/src/stores/wellKnown.tsx
deleted file mode 100644
index f3f9d2d..0000000
--- a/pkg/views/src/stores/wellKnown.tsx
+++ /dev/null
@@ -1,23 +0,0 @@
-import { createContext, useContext, useState } from "react";
-import { request } from "../scripts/request.ts";
-
-const WellKnownContext = createContext(null);
-
-export function WellKnownProvider(props: any) {
- const [wellKnown, setWellKnown] = useState(null);
-
- async function readWellKnown() {
- const res = await request("/.well-known");
- setWellKnown(await res.json());
- }
-
- return (
-
- {props.children}
-
- );
-}
-
-export function useWellKnown() {
- return useContext(WellKnownContext);
-}
\ No newline at end of file
diff --git a/pkg/views/src/theme.ts b/pkg/views/src/theme.ts
deleted file mode 100644
index 0c38afc..0000000
--- a/pkg/views/src/theme.ts
+++ /dev/null
@@ -1,20 +0,0 @@
-import { createTheme } from "@mui/material/styles";
-
-export const theme = createTheme({
- palette: {
- primary: {
- main: "#49509e",
- },
- secondary: {
- main: "#d43630",
- },
- },
- typography: {
- h1: { fontSize: "2.5rem" },
- h2: { fontSize: "2rem" },
- h3: { fontSize: "1.75rem" },
- h4: { fontSize: "1.5rem" },
- h5: { fontSize: "1.25rem" },
- h6: { fontSize: "1.15rem" },
- },
-});
diff --git a/pkg/views/src/views/auth/claims.ts b/pkg/views/src/views/auth/claims.ts
new file mode 100644
index 0000000..6ca79e5
--- /dev/null
+++ b/pkg/views/src/views/auth/claims.ts
@@ -0,0 +1,13 @@
+export interface ClaimType {
+ icon: string
+ name: string
+ description: string
+}
+
+export const claims: { [id: string]: ClaimType } = {
+ openid: {
+ icon: "mdi-identifier",
+ name: "Open Identity",
+ description: "Allow them to read your personal information.",
+ },
+}
diff --git a/pkg/views/src/views/auth/connect.vue b/pkg/views/src/views/auth/connect.vue
new file mode 100644
index 0000000..ea62fb0
--- /dev/null
+++ b/pkg/views/src/views/auth/connect.vue
@@ -0,0 +1,192 @@
+
+
+
+
+
+
+
Connect to third-party
+
One Solarpass, entire internet.
+
+
+
+
+
+
+
+ Something went wrong... {{ error }}
+
+
+
+ It's usually not our fault. Try bringing this link to give feedback to the developer of the app you
+ came from.
+
+
+
+
+
+
{{ metadata?.name ?? "Loading" }}
+
{{ metadata?.description ?? "Hold on a second please!" }}
+
+
+
Permissions they requested
+
+
+
+
+ {{ getClaimDescription(claim)?.name }}
+
+
+ {{ getClaimDescription(claim)?.description }}
+
+
+
+
+
+
+
+
+
+
+ Decline
+
+
+ Approve
+
+
+
+
+
After approve their request, you will be redirect to
+
{{ route.query["redirect_uri"] }}
+
+
+
+
+
+
+
+
+
+
+
Authoirzed
+
You're done! We sucessfully established connection between you and {{ metadata?.name }}.
+
+
Now you can continue your their app, we will redirect you soon.
+
+
Teleporting you to...
+
{{ route.query["redirect_uri"] }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/views/auth/sign-in.vue b/pkg/views/src/views/auth/sign-in.vue
new file mode 100644
index 0000000..f4b537b
--- /dev/null
+++ b/pkg/views/src/views/auth/sign-in.vue
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
Sign in
+
+
+
+
+
+ Risk: {{ challenge?.risk_level }}
+ Progress: {{ challenge?.progress }}/{{ challenge?.requirements }}
+
+
We need to verify that the person trying to access your account is you.
+
+
Sign in via your Solar ID to access the entire Solar Network.
+
+
+
+
+ (panel = val)"
+ v-model:loading="loading"
+ v-model:factors="factors"
+ v-model:currentFactor="currentFactor"
+ v-model:challenge="challenge"
+ />
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/views/auth/sign-up.vue b/pkg/views/src/views/auth/sign-up.vue
new file mode 100644
index 0000000..5efdfcb
--- /dev/null
+++ b/pkg/views/src/views/auth/sign-up.vue
@@ -0,0 +1,162 @@
+
+
+
+
+
+
+
+
+
Create an account
+
Create an account on Solar Network. Then enjoy all our services.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Something went wrong... {{ error }}
+
+
+
+
+
+ Sign in
+
+
+
+ Next
+
+
+
+
+
+
+
+
+
+
+ You successfully created an account on Solar Network. Now sign in to your account and start exploring!
+
+
+
+ Let's go
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/views/dashboard.vue b/pkg/views/src/views/dashboard.vue
new file mode 100644
index 0000000..b52e447
--- /dev/null
+++ b/pkg/views/src/views/dashboard.vue
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
{{ id.userinfo.displayName }}
+
What can I help you today?
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/src/views/personalize.vue b/pkg/views/src/views/personalize.vue
new file mode 100644
index 0000000..f46d427
--- /dev/null
+++ b/pkg/views/src/views/personalize.vue
@@ -0,0 +1,185 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Apply Changes
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Your personal information has been updated.
+
+
+
Something went wrong... {{ error }}
+
+
+
+
+
+
diff --git a/pkg/views/src/views/security.vue b/pkg/views/src/views/security.vue
new file mode 100644
index 0000000..979527f
--- /dev/null
+++ b/pkg/views/src/views/security.vue
@@ -0,0 +1,266 @@
+
+
+
+
+
+
+
+
+
+ {{ item.id }} |
+ {{ item.ip_address }} |
+
+
+
+
+ {{ item.user_agent }}
+
+
+
+ |
+ {{ new Date(item.created_at).toLocaleString() }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.id }} |
+
+
+ {{ value }}
+
+ |
+
+
+ {{ value }}
+
+ |
+ {{ new Date(item.created_at).toLocaleString() }} |
+
+
+
+
+
+
+ |
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ item.id }} |
+ {{ item.type }} |
+ {{ item.target }} |
+ {{ item.ip_address }} |
+
+
+
+
+ {{ item.user_agent }}
+
+
+
+ |
+ {{ new Date(item.created_at).toLocaleString() }} |
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/pkg/views/tsconfig.app.json b/pkg/views/tsconfig.app.json
new file mode 100644
index 0000000..e14c754
--- /dev/null
+++ b/pkg/views/tsconfig.app.json
@@ -0,0 +1,14 @@
+{
+ "extends": "@vue/tsconfig/tsconfig.dom.json",
+ "include": ["env.d.ts", "src/**/*", "src/**/*.vue"],
+ "exclude": ["src/**/__tests__/*"],
+ "compilerOptions": {
+ "composite": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.app.tsbuildinfo",
+
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["./src/*"]
+ }
+ }
+}
diff --git a/pkg/views/tsconfig.json b/pkg/views/tsconfig.json
index 67398cd..66b5e57 100644
--- a/pkg/views/tsconfig.json
+++ b/pkg/views/tsconfig.json
@@ -1,30 +1,11 @@
{
- "compilerOptions": {
- "target": "ES2020",
- "useDefineForClassFields": true,
- "lib": ["ES2020", "DOM", "DOM.Iterable"],
- "module": "ESNext",
- "skipLibCheck": true,
-
- /* Bundler mode */
- "moduleResolution": "bundler",
- "allowImportingTsExtensions": true,
- "resolveJsonModule": true,
- "isolatedModules": true,
- "noEmit": true,
- "jsx": "react-jsx",
-
- /* Linting */
- "strict": true,
- "noUnusedLocals": true,
- "noUnusedParameters": true,
- "noFallthroughCasesInSwitch": true,
-
- "baseUrl": "./src",
- "paths": {
- "@/*": ["./*"]
+ "files": [],
+ "references": [
+ {
+ "path": "./tsconfig.node.json"
+ },
+ {
+ "path": "./tsconfig.app.json"
}
- },
- "include": ["src"],
- "references": [{ "path": "./tsconfig.node.json" }]
+ ]
}
diff --git a/pkg/views/tsconfig.node.json b/pkg/views/tsconfig.node.json
index 97ede7e..2c669ee 100644
--- a/pkg/views/tsconfig.node.json
+++ b/pkg/views/tsconfig.node.json
@@ -1,11 +1,13 @@
{
+ "extends": "@tsconfig/node20/tsconfig.json",
+ "include": ["vite.config.*", "vitest.config.*", "cypress.config.*", "nightwatch.conf.*", "playwright.config.*"],
"compilerOptions": {
"composite": true,
- "skipLibCheck": true,
+ "noEmit": true,
+ "tsBuildInfoFile": "./node_modules/.tmp/tsconfig.node.tsbuildinfo",
+
"module": "ESNext",
- "moduleResolution": "bundler",
- "allowSyntheticDefaultImports": true,
- "strict": true
- },
- "include": ["vite.config.ts"]
+ "moduleResolution": "Bundler",
+ "types": ["node"]
+ }
}
diff --git a/pkg/views/uno.config.ts b/pkg/views/uno.config.ts
index b6816ad..2d323f7 100644
--- a/pkg/views/uno.config.ts
+++ b/pkg/views/uno.config.ts
@@ -1,5 +1,5 @@
-import { defineConfig, presetUno } from "unocss";
+import { defineConfig, presetAttributify, presetTypography, presetUno } from "unocss"
export default defineConfig({
- presets: [presetUno({ preflight: false })]
-});
\ No newline at end of file
+ presets: [presetAttributify(), presetTypography(), presetUno({ preflight: false })],
+})
diff --git a/pkg/views/vite.config.ts b/pkg/views/vite.config.ts
index b707ccd..3329c64 100644
--- a/pkg/views/vite.config.ts
+++ b/pkg/views/vite.config.ts
@@ -1,20 +1,22 @@
-import { defineConfig } from 'vite'
-import path from "path";
-import react from '@vitejs/plugin-react-swc'
+import { fileURLToPath, URL } from "node:url"
+
+import { defineConfig } from "vite"
+import vue from "@vitejs/plugin-vue"
+import vueJsx from "@vitejs/plugin-vue-jsx"
import unocss from "unocss/vite"
// https://vitejs.dev/config/
export default defineConfig({
- plugins: [react(), unocss()],
+ plugins: [vue(), vueJsx(), unocss()],
resolve: {
alias: {
- "@": path.resolve(__dirname, "./src"),
+ "@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
server: {
proxy: {
- "/.well-known": "http://localhost:8444",
- "/api": "http://localhost:8444"
+ "/api": "http://localhost:8444",
+ "/.well-known": "http://localhost:8444"
}
}
})