✨ User login
This commit is contained in:
parent
d484b6b973
commit
4f33b9e0f6
57
.idea/codeStyles/Project.xml
Normal file
57
.idea/codeStyles/Project.xml
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<HTMLCodeStyleSettings>
|
||||||
|
<option name="HTML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
</HTMLCodeStyleSettings>
|
||||||
|
<JSCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
</JSCodeStyleSettings>
|
||||||
|
<TypeScriptCodeStyleSettings version="0">
|
||||||
|
<option name="FORCE_SEMICOLON_STYLE" value="true" />
|
||||||
|
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
|
||||||
|
<option name="FORCE_QUOTE_STYlE" value="true" />
|
||||||
|
<option name="ENFORCE_TRAILING_COMMA" value="Remove" />
|
||||||
|
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
|
||||||
|
<option name="SPACES_WITHIN_IMPORTS" value="true" />
|
||||||
|
</TypeScriptCodeStyleSettings>
|
||||||
|
<VueCodeStyleSettings>
|
||||||
|
<option name="INTERPOLATION_NEW_LINE_AFTER_START_DELIMITER" value="false" />
|
||||||
|
<option name="INTERPOLATION_NEW_LINE_BEFORE_END_DELIMITER" value="false" />
|
||||||
|
</VueCodeStyleSettings>
|
||||||
|
<codeStyleSettings language="HTML">
|
||||||
|
<option name="SOFT_MARGINS" value="120" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="JavaScript">
|
||||||
|
<option name="SOFT_MARGINS" value="120" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="TypeScript">
|
||||||
|
<option name="SOFT_MARGINS" value="120" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="INDENT_SIZE" value="2" />
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
<option name="TAB_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="Vue">
|
||||||
|
<option name="SOFT_MARGINS" value="120" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="2" />
|
||||||
|
</indentOptions>
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
6
.idea/vcs.xml
Normal file
6
.idea/vcs.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
@ -52,6 +52,7 @@ type AuthChallengeState = int8
|
|||||||
|
|
||||||
const (
|
const (
|
||||||
ActiveChallengeState = AuthChallengeState(iota)
|
ActiveChallengeState = AuthChallengeState(iota)
|
||||||
|
ExpiredChallengeState
|
||||||
FinishChallengeState
|
FinishChallengeState
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -56,6 +56,8 @@ func NewChallenge(account models.Account, factors []models.AuthFactor, ip, ua st
|
|||||||
|
|
||||||
func DoChallenge(challenge models.AuthChallenge, factor models.AuthFactor, code string) error {
|
func DoChallenge(challenge models.AuthChallenge, factor models.AuthFactor, code string) error {
|
||||||
if err := challenge.IsAvailable(); err != nil {
|
if err := challenge.IsAvailable(); err != nil {
|
||||||
|
challenge.State = models.ExpiredChallengeState
|
||||||
|
database.C.Save(&challenge)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if challenge.Progress >= challenge.Requirements {
|
if challenge.Progress >= challenge.Requirements {
|
||||||
|
@ -7,13 +7,13 @@ import (
|
|||||||
"github.com/samber/lo"
|
"github.com/samber/lo"
|
||||||
)
|
)
|
||||||
|
|
||||||
func GetFactorCode(factor models.AuthFactor) error {
|
func GetFactorCode(factor models.AuthFactor) (bool, error) {
|
||||||
switch factor.Type {
|
switch factor.Type {
|
||||||
case models.EmailPasswordFactor:
|
case models.EmailPasswordFactor:
|
||||||
// TODO
|
// TODO
|
||||||
return nil
|
return true, nil
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("unsupported factor to get code")
|
return false, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ func doChallenge(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
func exchangeToken(c *fiber.Ctx) error {
|
func exchangeToken(c *fiber.Ctx) error {
|
||||||
var data struct {
|
var data struct {
|
||||||
Token string `json:"token"`
|
Code string `json:"code"`
|
||||||
GrantType string `json:"grant_type"`
|
GrantType string `json:"grant_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -99,7 +99,7 @@ func exchangeToken(c *fiber.Ctx) error {
|
|||||||
|
|
||||||
switch data.GrantType {
|
switch data.GrantType {
|
||||||
case "authorization_code":
|
case "authorization_code":
|
||||||
access, refresh, err := security.ExchangeToken(data.Token)
|
access, refresh, err := security.ExchangeToken(data.Code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
@ -109,7 +109,7 @@ func exchangeToken(c *fiber.Ctx) error {
|
|||||||
"refresh_token": refresh,
|
"refresh_token": refresh,
|
||||||
})
|
})
|
||||||
case "refresh_token":
|
case "refresh_token":
|
||||||
access, refresh, err := security.RefreshToken(data.Token)
|
access, refresh, err := security.RefreshToken(data.Code)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
return fiber.NewError(fiber.StatusBadRequest, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -14,9 +14,11 @@ func requestFactorToken(c *fiber.Ctx) error {
|
|||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := security.GetFactorCode(factor); err != nil {
|
if sent, err := security.GetFactorCode(factor); err != nil {
|
||||||
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
return fiber.NewError(fiber.StatusNotFound, err.Error())
|
||||||
}
|
} else if !sent {
|
||||||
|
return c.SendStatus(fiber.StatusNoContent)
|
||||||
|
} else {
|
||||||
return c.SendStatus(fiber.StatusOK)
|
return c.SendStatus(fiber.StatusOK)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,7 +29,7 @@ func LookupAccount(id string) (models.Account, error) {
|
|||||||
if err := database.C.
|
if err := database.C.
|
||||||
Where(models.Account{
|
Where(models.Account{
|
||||||
BaseModel: models.BaseModel{ID: contact.AccountID},
|
BaseModel: models.BaseModel{ID: contact.AccountID},
|
||||||
}).First(&contact).Error; err == nil {
|
}).First(&account).Error; err == nil {
|
||||||
return account, err
|
return account, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
24
view/.gitignore
vendored
Normal file
24
view/.gitignore
vendored
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
dist
|
||||||
|
dist-ssr
|
||||||
|
*.local
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
.DS_Store
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
28
view/README.md
Normal file
28
view/README.md
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
## Usage
|
||||||
|
|
||||||
|
```bash
|
||||||
|
$ npm install # or pnpm install or yarn install
|
||||||
|
```
|
||||||
|
|
||||||
|
### Learn more on the [Solid Website](https://solidjs.com) and come chat with us on our [Discord](https://discord.com/invite/solidjs)
|
||||||
|
|
||||||
|
## Available Scripts
|
||||||
|
|
||||||
|
In the project directory, you can run:
|
||||||
|
|
||||||
|
### `npm run dev`
|
||||||
|
|
||||||
|
Runs the app in the development mode.<br>
|
||||||
|
Open [http://localhost:5173](http://localhost:5173) to view it in the browser.
|
||||||
|
|
||||||
|
### `npm run build`
|
||||||
|
|
||||||
|
Builds the app for production to the `dist` folder.<br>
|
||||||
|
It correctly bundles Solid in production mode and optimizes the build for the best performance.
|
||||||
|
|
||||||
|
The build is minified and the filenames include the hashes.<br>
|
||||||
|
Your app is ready to be deployed!
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Learn more about deploying your application with the [documentations](https://vitejs.dev/guide/static-deploy.html)
|
13
view/index.html
Normal file
13
view/index.html
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
|
<title>Goatpass</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="root"></div>
|
||||||
|
<script type="module" src="/src/index.tsx"></script>
|
||||||
|
</body>
|
||||||
|
</html>
|
25
view/package.json
Normal file
25
view/package.json
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
"name": "@hydrogen/passport-web",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.0",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite",
|
||||||
|
"build": "tsc && vite build",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@solidjs/router": "^0.10.10",
|
||||||
|
"solid-js": "^1.8.7",
|
||||||
|
"universal-cookie": "^7.0.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"autoprefixer": "^10.4.17",
|
||||||
|
"daisyui": "^4.6.0",
|
||||||
|
"postcss": "^8.4.33",
|
||||||
|
"tailwindcss": "^3.4.1",
|
||||||
|
"typescript": "^5.2.2",
|
||||||
|
"vite": "^5.0.8",
|
||||||
|
"vite-plugin-solid": "^2.8.0"
|
||||||
|
}
|
||||||
|
}
|
6
view/postcss.config.js
Normal file
6
view/postcss.config.js
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
tailwindcss: {},
|
||||||
|
autoprefixer: {},
|
||||||
|
},
|
||||||
|
}
|
5
view/src/.prettierrc
Normal file
5
view/src/.prettierrc
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 120,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"singleQuote": false
|
||||||
|
}
|
197
view/src/assets/fonts/fonts.css
Normal file
197
view/src/assets/fonts/fonts.css
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
:root {
|
||||||
|
--bs-body-font-family: "IBM Plex Serif", "Noto Serif SC", sans-serif !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
font-family: var(--bs-body-font-family);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-100 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 100;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-100.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-100italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 100;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-100italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-200 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-200italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 200;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-200italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-300 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-300italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-300italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-regular - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-500 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-500italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 500;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-500italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-600 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-600italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-600italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-700 - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ibm-plex-serif-700italic - latin */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "IBM Plex Serif";
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("./ibm-plex-serif-v19-latin-700italic.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-200 - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 200;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-200.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-300 - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-300.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-regular - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-regular.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-500 - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 500;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-500.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-600 - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-600.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-700 - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-700.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* noto-serif-sc-900 - chinese-simplified */
|
||||||
|
@font-face {
|
||||||
|
font-display: swap; /* Check https://developer.mozilla.org/en-US/docs/Web/CSS/@font-face/font-display for other options. */
|
||||||
|
font-family: "Noto Serif SC";
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 900;
|
||||||
|
src: url("./noto-serif-sc-v22-chinese-simplified-900.woff2") format("woff2"); /* Chrome 36+, Opera 23+, Firefox 39+, Safari 12+, iOS 10+ */
|
||||||
|
}
|
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-100.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-100.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-100italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-100italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-200.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-200.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-200italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-200italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-300.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-300.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-300italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-300italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-500.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-500.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-500italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-500italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-600.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-600.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-600italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-600italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-700.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-700.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-700italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-700italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-italic.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-italic.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-regular.woff2
Executable file
BIN
view/src/assets/fonts/ibm-plex-serif-v19-latin-regular.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-200.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-300.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-500.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-600.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-700.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-900.woff2
Executable file
Binary file not shown.
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
Executable file
BIN
view/src/assets/fonts/noto-serif-sc-v22-chinese-simplified-regular.woff2
Executable file
Binary file not shown.
9
view/src/index.css
Normal file
9
view/src/index.css
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
@tailwind base;
|
||||||
|
@tailwind components;
|
||||||
|
@tailwind utilities;
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0;
|
||||||
|
height: 100vh;
|
||||||
|
}
|
19
view/src/index.tsx
Normal file
19
view/src/index.tsx
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
/* @refresh reload */
|
||||||
|
import { render } from "solid-js/web";
|
||||||
|
|
||||||
|
import "./index.css";
|
||||||
|
import "./assets/fonts/fonts.css";
|
||||||
|
import { Route, Router } from "@solidjs/router";
|
||||||
|
|
||||||
|
import RootLayout from "./layouts/RootLayout.tsx";
|
||||||
|
import Dashboard from "./pages/dashboard.tsx";
|
||||||
|
import Login from "./pages/auth/login.tsx";
|
||||||
|
|
||||||
|
const root = document.getElementById("root");
|
||||||
|
|
||||||
|
render(() => (
|
||||||
|
<Router root={RootLayout}>
|
||||||
|
<Route path="/" component={Dashboard} />
|
||||||
|
<Route path="/auth/login" component={Login} />
|
||||||
|
</Router>
|
||||||
|
), root!);
|
11
view/src/layouts/RootLayout.tsx
Normal file
11
view/src/layouts/RootLayout.tsx
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import Navbar from "./shared/Navbar.tsx";
|
||||||
|
|
||||||
|
export default function RootLayout(props: any) {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Navbar />
|
||||||
|
|
||||||
|
<main class="h-[calc(100vh-68px)]">{props.children}</main>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
64
view/src/layouts/shared/Navbar.tsx
Normal file
64
view/src/layouts/shared/Navbar.tsx
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
import { For } from "solid-js";
|
||||||
|
|
||||||
|
interface MenuItem {
|
||||||
|
label: string;
|
||||||
|
href: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Navbar() {
|
||||||
|
const nav: MenuItem[] = [{ label: "Dashboard", href: "/" }];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="navbar bg-base-100 shadow-md px-5">
|
||||||
|
<div class="navbar-start">
|
||||||
|
<div class="dropdown">
|
||||||
|
<div tabIndex={0} role="button" class="btn btn-ghost lg:hidden">
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-5 w-5"
|
||||||
|
fill="none"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
stroke="currentColor"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="2"
|
||||||
|
d="M4 6h16M4 12h8m-8 6h16"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<ul
|
||||||
|
tabIndex={0}
|
||||||
|
class="menu menu-sm dropdown-content mt-3 z-[1] p-2 shadow bg-base-100 rounded-box w-52"
|
||||||
|
>
|
||||||
|
<For each={nav}>
|
||||||
|
{(item) => (
|
||||||
|
<li>
|
||||||
|
<a href={item.href}>{item.label}</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<a href="/" class="btn btn-ghost text-xl">
|
||||||
|
Goatpass
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-center hidden lg:flex">
|
||||||
|
<ul class="menu menu-horizontal px-1">
|
||||||
|
<For each={nav}>
|
||||||
|
{(item) => (
|
||||||
|
<li>
|
||||||
|
<a href={item.href}>{item.label}</a>
|
||||||
|
</li>
|
||||||
|
)}
|
||||||
|
</For>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
<div class="navbar-end pe-5">
|
||||||
|
<a href="/auth/login" class="btn btn-sm btn-primary">Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
202
view/src/pages/auth/login.tsx
Normal file
202
view/src/pages/auth/login.tsx
Normal file
@ -0,0 +1,202 @@
|
|||||||
|
import { useNavigate } from "@solidjs/router";
|
||||||
|
import { createSignal, For, Match, Show, Switch } from "solid-js";
|
||||||
|
import Cookie from "universal-cookie";
|
||||||
|
|
||||||
|
export default function Login() {
|
||||||
|
const [title, setTitle] = createSignal("Sign in");
|
||||||
|
const [subtitle, setSubtitle] = createSignal("Via your Goatpass account");
|
||||||
|
|
||||||
|
const [error, setError] = createSignal<null | string>(null);
|
||||||
|
const [loading, setLoading] = createSignal(false);
|
||||||
|
|
||||||
|
const [factor, setFactor] = createSignal<number>();
|
||||||
|
const [factors, setFactors] = createSignal<any[]>([]);
|
||||||
|
const [challenge, setChallenge] = createSignal<any>();
|
||||||
|
const [stage, setStage] = createSignal("starting");
|
||||||
|
|
||||||
|
const navigate = useNavigate();
|
||||||
|
|
||||||
|
const handlers: { [id: string]: any } = {
|
||||||
|
"starting": async (evt: SubmitEvent) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
|
||||||
|
if (!data.id) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const res = await fetch("/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();
|
||||||
|
setTitle(`Welcome, ${data["display_name"]}`);
|
||||||
|
setSubtitle("Before continue, we need verify that's you");
|
||||||
|
setFactors(data["factors"]);
|
||||||
|
setChallenge(data["challenge"]);
|
||||||
|
setError(null);
|
||||||
|
setStage("choosing");
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
},
|
||||||
|
"choosing": async (evt: SubmitEvent) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
|
||||||
|
if (!data.factor) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const res = await fetch(`/api/auth/factors/${data.id}`, {
|
||||||
|
method: "POST"
|
||||||
|
});
|
||||||
|
if (res.status !== 200 && res.status !== 204) {
|
||||||
|
setError(await res.text());
|
||||||
|
} else {
|
||||||
|
setTitle(`Enter the code`);
|
||||||
|
setSubtitle(res.status === 204 ? "Enter your credentials" : "Code has been sent to your inbox");
|
||||||
|
setError(null);
|
||||||
|
setFactor(parseInt(data.factor as string));
|
||||||
|
setStage("verifying");
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
},
|
||||||
|
"verifying": async (evt: SubmitEvent) => {
|
||||||
|
evt.preventDefault();
|
||||||
|
|
||||||
|
const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement));
|
||||||
|
if (!data.credentials) return;
|
||||||
|
|
||||||
|
setLoading(true);
|
||||||
|
const res = await fetch(`/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"]);
|
||||||
|
navigate("/");
|
||||||
|
} else {
|
||||||
|
setError(null);
|
||||||
|
setStage("choosing");
|
||||||
|
setTitle("Continue verifying");
|
||||||
|
setSubtitle("You passed one check, but that's not enough.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function grantToken(tk: string) {
|
||||||
|
const res = await fetch("/api/auth/token", {
|
||||||
|
method: "POST",
|
||||||
|
headers: { "Content-Type": "application/json" },
|
||||||
|
body: JSON.stringify({
|
||||||
|
code: tk,
|
||||||
|
grant_type: "authorization_code"
|
||||||
|
})
|
||||||
|
});
|
||||||
|
if (res.status !== 200) {
|
||||||
|
const err = await res.text();
|
||||||
|
setError(err);
|
||||||
|
throw new Error(err);
|
||||||
|
} else {
|
||||||
|
const data = await res.json();
|
||||||
|
new Cookie().set("access_token", data["access_token"], { path: "/" });
|
||||||
|
new Cookie().set("refresh_token", data["refresh_token"], { path: "/" });
|
||||||
|
setError(null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getFactorName(factor: any) {
|
||||||
|
switch (factor.type) {
|
||||||
|
case 0:
|
||||||
|
return "Password Verification";
|
||||||
|
default:
|
||||||
|
return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div class="w-full h-full flex justify-center items-center">
|
||||||
|
<div class="card w-[480px] max-w-screen shadow-xl">
|
||||||
|
<div class="card-body">
|
||||||
|
<div id="header" class="text-center mb-5">
|
||||||
|
<h1 class="text-xl font-bold">{title()}</h1>
|
||||||
|
<p>{subtitle()}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Show when={error()}>
|
||||||
|
<div id="alerts" class="mt-1">
|
||||||
|
<div role="alert" class="alert alert-error">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" class="stroke-current shrink-0 h-6 w-6" fill="none"
|
||||||
|
viewBox="0 0 24 24">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
|
||||||
|
d="M10 14l2-2m0 0l2-2m-2 2l-2-2m2 2l2 2m7-2a9 9 0 11-18 0 9 9 0 0118 0z" />
|
||||||
|
</svg>
|
||||||
|
<span class="capitalize">{error()}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<form id="form" onSubmit={(e) => handlers[stage()](e)}>
|
||||||
|
<Switch>
|
||||||
|
<Match when={stage() === "starting"}>
|
||||||
|
<label class="form-control w-full">
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text">Account ID</span>
|
||||||
|
</div>
|
||||||
|
<input name="id" type="text" placeholder="Type here" class="input input-bordered w-full" />
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text-alt">Your username, email or phone number.</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</Match>
|
||||||
|
<Match when={stage() === "choosing"}>
|
||||||
|
<div class="join join-vertical w-full">
|
||||||
|
<For each={factors()}>
|
||||||
|
{item =>
|
||||||
|
<input class="join-item btn" type="radio" name="factor"
|
||||||
|
value={item.id}
|
||||||
|
aria-label={getFactorName(item)}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
<p class="text-center text-sm mt-2">Choose a way to verify that's you</p>
|
||||||
|
</Match>
|
||||||
|
<Match when={stage() === "verifying"}>
|
||||||
|
<label class="form-control w-full">
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text">Credentials</span>
|
||||||
|
</div>
|
||||||
|
<input name="credentials" type="password" placeholder="Type here"
|
||||||
|
class="input input-bordered w-full" />
|
||||||
|
<div class="label">
|
||||||
|
<span class="label-text-alt">Password or one time password.</span>
|
||||||
|
</div>
|
||||||
|
</label>
|
||||||
|
</Match>
|
||||||
|
</Switch>
|
||||||
|
|
||||||
|
<button type="submit" class="btn btn-primary btn-block mt-3" disabled={loading()}>
|
||||||
|
<Show when={loading()} fallback={"Next"}>
|
||||||
|
<span class="loading loading-spinner"></span>
|
||||||
|
</Show>
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
8
view/src/pages/dashboard.tsx
Normal file
8
view/src/pages/dashboard.tsx
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export default function Dashboard() {
|
||||||
|
return (
|
||||||
|
<div class="container mx-auto pt-12">
|
||||||
|
<h1 class="text-2xl font-bold">Welcome, undefined</h1>
|
||||||
|
<p>What's a nice day!</p>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
1
view/src/vite-env.d.ts
vendored
Normal file
1
view/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
/// <reference types="vite/client" />
|
44
view/tailwind.config.js
Normal file
44
view/tailwind.config.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
/** @type {import("tailwindcss").Config} */
|
||||||
|
export default {
|
||||||
|
content: [
|
||||||
|
"./src/**/*.{js,jsx,ts,tsx}"
|
||||||
|
],
|
||||||
|
daisyui: {
|
||||||
|
themes: [
|
||||||
|
{
|
||||||
|
light: {
|
||||||
|
...require("daisyui/src/theming/themes")["light"],
|
||||||
|
primary: "#4750a3",
|
||||||
|
secondary: "#93c5fd",
|
||||||
|
accent: "#0f766e",
|
||||||
|
info: "#67e8f9",
|
||||||
|
success: "#15803d",
|
||||||
|
warning: "#f97316",
|
||||||
|
error: "#dc2626",
|
||||||
|
"--rounded-box": "0",
|
||||||
|
"--rounded-btn": "0",
|
||||||
|
"--rounded-badge": "0",
|
||||||
|
"--tab-radius": "0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
dark: {
|
||||||
|
...require("daisyui/src/theming/themes")["dark"],
|
||||||
|
primary: "#4750a3",
|
||||||
|
secondary: "#93c5fd",
|
||||||
|
accent: "#0f766e",
|
||||||
|
info: "#67e8f9",
|
||||||
|
success: "#15803d",
|
||||||
|
warning: "#f97316",
|
||||||
|
error: "#dc2626",
|
||||||
|
"--rounded-box": "0",
|
||||||
|
"--rounded-btn": "0",
|
||||||
|
"--rounded-badge": "0",
|
||||||
|
"--tab-radius": "0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
plugins: [require("daisyui")]
|
||||||
|
};
|
||||||
|
|
26
view/tsconfig.json
Normal file
26
view/tsconfig.json
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ES2020",
|
||||||
|
"useDefineForClassFields": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"lib": ["ES2020", "DOM", "DOM.Iterable"],
|
||||||
|
"skipLibCheck": true,
|
||||||
|
|
||||||
|
/* Bundler mode */
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"isolatedModules": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "solid-js",
|
||||||
|
|
||||||
|
/* Linting */
|
||||||
|
"strict": true,
|
||||||
|
"noUnusedLocals": true,
|
||||||
|
"noUnusedParameters": true,
|
||||||
|
"noFallthroughCasesInSwitch": true
|
||||||
|
},
|
||||||
|
"include": ["src"],
|
||||||
|
"references": [{ "path": "./tsconfig.node.json" }]
|
||||||
|
}
|
10
view/tsconfig.node.json
Normal file
10
view/tsconfig.node.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"composite": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true
|
||||||
|
},
|
||||||
|
"include": ["vite.config.ts"]
|
||||||
|
}
|
11
view/vite.config.ts
Normal file
11
view/vite.config.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { defineConfig } from 'vite'
|
||||||
|
import solid from 'vite-plugin-solid'
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [solid()],
|
||||||
|
server: {
|
||||||
|
proxy: {
|
||||||
|
"/api": "http://localhost:8444"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
1517
view/yarn.lock
Normal file
1517
view/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user