20 Commits

Author SHA1 Message Date
09f7c33135 chore: update version 2023-09-30 22:57:47 +08:00
fe3b78f844 chore: update i18n 2023-09-30 22:57:30 +08:00
0fd54426e6 chore: add create shortcut button when not found 2023-09-30 22:03:18 +08:00
690e14e4ed chore: update zh i18n 2023-09-30 09:03:52 +08:00
7795b17fd1 chore: add tags quick selector 2023-09-30 01:48:29 +08:00
c7dd4dc3eb chore: update extension version 2023-09-30 01:29:46 +08:00
6ee6a5166e chore: update dev server config 2023-09-30 01:23:25 +08:00
8c753e9557 feat: impl dark mode for extension 2023-09-30 01:22:40 +08:00
6126701025 chore: update favicon getter 2023-09-29 21:34:23 +08:00
8ef7d5f0d0 chore: update shortcut response styles 2023-09-29 21:17:06 +08:00
fa8d2f6639 chore: remove resource relative path setting 2023-09-29 19:55:13 +08:00
8cd976791e feat: get url favicon from google s2 2023-09-29 19:37:44 +08:00
010271c668 chore: update frontend deps 2023-09-29 19:10:01 +08:00
383d4f27f0 chore: fix subscription link 2023-09-25 06:18:59 +08:00
cb9786ef7c chore: fix title style in dark mode 2023-09-25 06:08:43 +08:00
e936bb6f15 chore: update subscription permission 2023-09-25 06:08:30 +08:00
60c440ae10 chore: fix check enable signup 2023-09-24 23:46:49 +08:00
fc8808ce04 chore: fix enable signup default value 2023-09-24 23:29:20 +08:00
e88327f2a3 chore: update resource service folder 2023-09-24 22:55:31 +08:00
159dfc9446 chore: add useNavigateTo hook 2023-09-24 21:21:34 +08:00
61 changed files with 1489 additions and 1476 deletions

View File

@ -36,15 +36,15 @@ func extractTokenFromHeader(c echo.Context) (string, error) {
} }
func findAccessToken(c echo.Context) string { func findAccessToken(c echo.Context) string {
accessToken := "" // Check the HTTP request header first.
cookie, _ := c.Cookie(auth.AccessTokenCookieName) accessToken, _ := extractTokenFromHeader(c)
if cookie != nil {
accessToken = cookie.Value
}
if accessToken == "" { if accessToken == "" {
accessToken, _ = extractTokenFromHeader(c) // Check the cookie.
cookie, _ := c.Cookie(auth.AccessTokenCookieName)
if cookie != nil {
accessToken = cookie.Value
}
} }
return accessToken return accessToken
} }

View File

@ -30,7 +30,7 @@ func (s *APIV1Service) registerRedirectorRoutes(g *echo.Group) {
return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to get shortcut, err: %s", err)).SetInternal(err) return echo.NewHTTPError(http.StatusInternalServerError, fmt.Sprintf("failed to get shortcut, err: %s", err)).SetInternal(err)
} }
if shortcut == nil { if shortcut == nil {
return echo.NewHTTPError(http.StatusNotFound, fmt.Sprintf("not found shortcut with name: %s", shortcutName)) return c.Redirect(http.StatusSeeOther, fmt.Sprintf("/404?shortcut=%s", shortcutName))
} }
if shortcut.Visibility != storepb.Visibility_PUBLIC { if shortcut.Visibility != storepb.Visibility_PUBLIC {
userID, ok := c.Get(userIDContextKey).(int32) userID, ok := c.Get(userIDContextKey).(int32)

View File

@ -1,31 +0,0 @@
package v1
import (
"fmt"
"net/http"
"github.com/labstack/echo/v4"
"go.deanishe.net/favicon"
)
func (*APIV1Service) registerURLUtilRoutes(g *echo.Group) {
// GET /url/favicon?url=...
g.GET("/url/favicon", func(c echo.Context) error {
url := c.QueryParam("url")
icons, err := favicon.Find(url)
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("failed to find favicon, err: %s", err))
}
availableIcons := []*favicon.Icon{}
for _, icon := range icons {
if icon.Width == icon.Height {
availableIcons = append(availableIcons, icon)
}
}
if len(availableIcons) == 0 {
return echo.NewHTTPError(http.StatusNotFound, "no favicon found")
}
return c.JSON(http.StatusOK, availableIcons[0].URL)
})
}

View File

@ -27,7 +27,6 @@ func (s *APIV1Service) Start(apiGroup *echo.Group, secret string) {
apiV1Group.Use(func(next echo.HandlerFunc) echo.HandlerFunc { apiV1Group.Use(func(next echo.HandlerFunc) echo.HandlerFunc {
return JWTMiddleware(s, next, secret) return JWTMiddleware(s, next, secret)
}) })
s.registerURLUtilRoutes(apiV1Group)
s.registerWorkspaceRoutes(apiV1Group) s.registerWorkspaceRoutes(apiV1Group)
s.registerAuthRoutes(apiV1Group, secret) s.registerAuthRoutes(apiV1Group, secret)
s.registerUserRoutes(apiV1Group) s.registerUserRoutes(apiV1Group)

View File

@ -4,6 +4,7 @@ import "strings"
var allowedMethodsWhenUnauthorized = map[string]bool{ var allowedMethodsWhenUnauthorized = map[string]bool{
"/slash.api.v2.WorkspaceService/GetWorkspaceProfile": true, "/slash.api.v2.WorkspaceService/GetWorkspaceProfile": true,
"/slash.api.v2.WorkspaceService/GetWorkspaceSetting": true,
} }
// isUnauthorizeAllowedMethod returns true if the method is allowed to be called when the user is not authorized. // isUnauthorizeAllowedMethod returns true if the method is allowed to be called when the user is not authorized.

View File

@ -74,7 +74,9 @@ func (s *WorkspaceService) GetWorkspaceSetting(ctx context.Context, _ *apiv2pb.G
if err != nil { if err != nil {
return nil, status.Errorf(codes.Internal, "failed to list workspace settings: %v", err) return nil, status.Errorf(codes.Internal, "failed to list workspace settings: %v", err)
} }
workspaceSetting := &apiv2pb.WorkspaceSetting{} workspaceSetting := &apiv2pb.WorkspaceSetting{
EnableSignup: true,
}
for _, v := range workspaceSettings { for _, v := range workspaceSettings {
if v.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP { if v.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP {
workspaceSetting.EnableSignup = v.GetEnableSignup() workspaceSetting.EnableSignup = v.GetEnableSignup()
@ -86,8 +88,6 @@ func (s *WorkspaceService) GetWorkspaceSetting(ctx context.Context, _ *apiv2pb.G
// For some settings, only admin can get the value. // For some settings, only admin can get the value.
if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_LICENSE_KEY { if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_LICENSE_KEY {
workspaceSetting.LicenseKey = v.GetLicenseKey() workspaceSetting.LicenseKey = v.GetLicenseKey()
} else if v.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH {
workspaceSetting.ResourceRelativePath = v.GetResourceRelativePath()
} }
} }
} }
@ -120,15 +120,6 @@ func (s *WorkspaceService) UpdateWorkspaceSetting(ctx context.Context, request *
}); err != nil { }); err != nil {
return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err) return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err)
} }
} else if path == "resource_relative_path" {
if _, err := s.Store.UpsertWorkspaceSetting(ctx, &storepb.WorkspaceSetting{
Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH,
Value: &storepb.WorkspaceSetting_ResourceRelativePath{
ResourceRelativePath: request.Setting.ResourceRelativePath,
},
}); err != nil {
return nil, status.Errorf(codes.Internal, "failed to update workspace setting: %v", err)
}
} else if path == "custom_style" { } else if path == "custom_style" {
if !s.LicenseService.IsFeatureEnabled(license.FeatureTypeCustomeStyle) { if !s.LicenseService.IsFeatureEnabled(license.FeatureTypeCustomeStyle) {
return nil, status.Errorf(codes.PermissionDenied, "feature custom style is not available") return nil, status.Errorf(codes.PermissionDenied, "feature custom style is not available")

View File

@ -1,7 +1,7 @@
{ {
"name": "slash-extension", "name": "slash-extension",
"displayName": "Slash", "displayName": "Slash",
"version": "0.1.4", "version": "1.0.0",
"description": "An open source, self-hosted bookmarks and link sharing platform. Save and share your links very easily.", "description": "An open source, self-hosted bookmarks and link sharing platform. Save and share your links very easily.",
"scripts": { "scripts": {
"dev": "plasmo dev", "dev": "plasmo dev",
@ -15,7 +15,7 @@
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/joy": "5.0.0-beta.0", "@mui/joy": "5.0.0-beta.0",
"@plasmohq/storage": "^1.8.0", "@plasmohq/storage": "^1.8.0",
"axios": "^1.5.0", "axios": "^1.5.1",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"lodash-es": "^4.17.21", "lodash-es": "^4.17.21",
"lucide-react": "^0.264.0", "lucide-react": "^0.264.0",
@ -32,15 +32,15 @@
"@types/node": "20.4.2", "@types/node": "20.4.2",
"@types/react": "18.2.15", "@types/react": "18.2.15",
"@types/react-dom": "18.2.7", "@types/react-dom": "18.2.7",
"@typescript-eslint/eslint-plugin": "^6.7.0", "@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.0", "@typescript-eslint/parser": "^6.7.3",
"autoprefixer": "^10.4.15", "autoprefixer": "^10.4.16",
"eslint": "^8.49.0", "eslint": "^8.50.0",
"eslint-config-prettier": "^8.10.0", "eslint-config-prettier": "^8.10.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"long": "^5.2.3", "long": "^5.2.3",
"postcss": "^8.4.29", "postcss": "^8.4.31",
"prettier": "2.6.2", "prettier": "2.6.2",
"protobufjs": "^7.2.5", "protobufjs": "^7.2.5",
"tailwindcss": "^3.3.3", "tailwindcss": "^3.3.3",

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,7 @@
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service"; import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { useStorage } from "@plasmohq/storage/hook"; import { useStorage } from "@plasmohq/storage/hook";
import classNames from "classnames"; import classNames from "classnames";
import { useEffect, useState } from "react"; import { getFaviconWithGoogleS2 } from "@/helpers/utils";
import useFaviconStore from "../stores/favicon";
import Icon from "./Icon"; import Icon from "./Icon";
interface Props { interface Props {
@ -11,17 +10,8 @@ interface Props {
const ShortcutView = (props: Props) => { const ShortcutView = (props: Props) => {
const { shortcut } = props; const { shortcut } = props;
const faviconStore = useFaviconStore();
const [domain] = useStorage<string>("domain", ""); const [domain] = useStorage<string>("domain", "");
const [favicon, setFavicon] = useState<string | undefined>(undefined); const favicon = getFaviconWithGoogleS2(shortcut.link);
useEffect(() => {
faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => {
if (url) {
setFavicon(url);
}
});
}, [shortcut.link]);
const handleShortcutLinkClick = () => { const handleShortcutLinkClick = () => {
const shortcutLink = `${domain}/s/${shortcut.name}`; const shortcutLink = `${domain}/s/${shortcut.name}`;
@ -32,7 +22,7 @@ const ShortcutView = (props: Props) => {
<> <>
<div <div
className={classNames( className={classNames(
"group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow" "group w-full px-3 py-2 flex flex-col justify-start items-start border rounded-lg hover:bg-gray-100 hover:shadow dark:border-zinc-800 dark:hover:bg-zinc-800"
)} )}
> >
<div className="w-full flex flex-row justify-start items-center"> <div className="w-full flex flex-row justify-start items-center">
@ -52,13 +42,13 @@ const ShortcutView = (props: Props) => {
onClick={handleShortcutLinkClick} onClick={handleShortcutLinkClick}
> >
<div className="truncate"> <div className="truncate">
<span>{shortcut.title}</span> <span className="dark:text-gray-400">{shortcut.title}</span>
{shortcut.title ? ( {shortcut.title ? (
<span className="text-gray-400">(s/{shortcut.name})</span> <span className="text-gray-500">(s/{shortcut.name})</span>
) : ( ) : (
<> <>
<span className="text-gray-400">s/</span> <span className="text-gray-400 dark:text-gray-500">s/</span>
<span className="truncate">{shortcut.name}</span> <span className="truncate dark:text-gray-400">{shortcut.name}</span>
</> </>
)} )}
</div> </div>

View File

@ -1,14 +0,0 @@
import { Storage } from "@plasmohq/storage";
import axios from "axios";
const storage = new Storage();
export const getUrlFavicon = async (url: string) => {
const domain = await storage.getItem<string>("domain");
const accessToken = await storage.getItem<string>("access_token");
return axios.get<string>(`${domain}/api/v1/url/favicon?url=${url}`, {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
};

View File

@ -3,3 +3,12 @@ import { isNull, isUndefined } from "lodash-es";
export const isNullorUndefined = (value: any) => { export const isNullorUndefined = (value: any) => {
return isNull(value) || isUndefined(value); return isNull(value) || isUndefined(value);
}; };
export const getFaviconWithGoogleS2 = (url: string) => {
try {
const urlObject = new URL(url);
return `https://www.google.com/s2/favicons?sz=128&domain=${urlObject.hostname}`;
} catch (error) {
return undefined;
}
};

View File

@ -0,0 +1,43 @@
import { useColorScheme } from "@mui/joy";
import { useEffect } from "react";
const useColorTheme = () => {
const { mode: colorTheme, setMode: setColorTheme } = useColorScheme();
useEffect(() => {
const root = document.documentElement;
if (colorTheme === "light") {
root.classList.remove("dark");
} else if (colorTheme === "dark") {
root.classList.add("dark");
} else {
const darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)");
if (darkMediaQuery.matches) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
const handleColorSchemeChange = (e: MediaQueryListEvent) => {
if (e.matches) {
root.classList.add("dark");
} else {
root.classList.remove("dark");
}
};
try {
darkMediaQuery.addEventListener("change", handleColorSchemeChange);
} catch (error) {
console.error("failed to initial color scheme listener", error);
}
return () => {
darkMediaQuery.removeEventListener("change", handleColorSchemeChange);
};
}
}, [colorTheme]);
return { colorTheme, setColorTheme };
};
export default useColorTheme;

View File

@ -1,5 +1,5 @@
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service"; import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { Button, Divider, Input } from "@mui/joy"; import { Button, CssVarsProvider, Divider, Input, Select, Option } from "@mui/joy";
import { useStorage } from "@plasmohq/storage/hook"; import { useStorage } from "@plasmohq/storage/hook";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { Toaster, toast } from "react-hot-toast"; import { Toaster, toast } from "react-hot-toast";
@ -7,6 +7,7 @@ import Icon from "./components/Icon";
import Logo from "./components/Logo"; import Logo from "./components/Logo";
import PullShortcutsButton from "./components/PullShortcutsButton"; import PullShortcutsButton from "./components/PullShortcutsButton";
import ShortcutsContainer from "./components/ShortcutsContainer"; import ShortcutsContainer from "./components/ShortcutsContainer";
import useColorTheme from "./hooks/useColorTheme";
import "./style.css"; import "./style.css";
interface SettingState { interface SettingState {
@ -14,7 +15,23 @@ interface SettingState {
accessToken: string; accessToken: string;
} }
const colorThemeOptions = [
{
value: "system",
label: "System",
},
{
value: "light",
label: "Light",
},
{
value: "dark",
label: "Dark",
},
];
const IndexOptions = () => { const IndexOptions = () => {
const { colorTheme, setColorTheme } = useColorTheme();
const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : "")); const [domain, setDomain] = useStorage<string>("domain", (v) => (v ? v : ""));
const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : "")); const [accessToken, setAccessToken] = useStorage<string>("access_token", (v) => (v ? v : ""));
const [settingState, setSettingState] = useState<SettingState>({ const [settingState, setSettingState] = useState<SettingState>({
@ -44,91 +61,119 @@ const IndexOptions = () => {
toast.success("Setting saved"); toast.success("Setting saved");
}; };
const handleSelectColorTheme = async (colorTheme: string) => {
setColorTheme(colorTheme as any);
};
return ( return (
<> <div className="w-full">
<div className="w-full"> <div className="w-full flex flex-row justify-center items-center">
<div className="w-full flex flex-row justify-center items-center"> <a
<a className="bg-yellow-100 dark:bg-yellow-500 dark:opacity-70 mt-12 py-2 px-3 rounded-full border dark:border-yellow-600 flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600"
className="bg-yellow-100 mt-12 py-2 px-3 rounded-full border flex flex-row justify-start items-center cursor-pointer shadow hover:underline hover:text-blue-600" href="https://github.com/boojack/slash#browser-extension"
href="https://github.com/boojack/slash#browser-extension" target="_blank"
target="_blank" >
> <Icon.HelpCircle className="w-4 h-auto" />
<Icon.HelpCircle className="w-4 h-auto" /> <span className="mx-1 text-sm">Need help? Check out the docs</span>
<span className="mx-1 text-sm">Need help? Check out the docs</span> <Icon.ExternalLink className="w-4 h-auto" />
<Icon.ExternalLink className="w-4 h-auto" /> </a>
</a> </div>
</div>
<div className="w-full max-w-lg mx-auto flex flex-col justify-start items-start mt-12"> <div className="w-full max-w-lg mx-auto flex flex-col justify-start items-start mt-12">
<h2 className="flex flex-row justify-start items-center mb-6 text-2xl"> <h2 className="flex flex-row justify-start items-center mb-6 text-2xl dark:text-gray-400">
<Logo className="w-10 h-auto mr-2" /> <Logo className="w-10 h-auto mr-2" />
<span>Slash</span> <span>Slash</span>
<span className="mx-2 text-gray-400">/</span> <span className="mx-2 text-gray-400">/</span>
<span>Setting</span> <span>Setting</span>
</h2> </h2>
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<div className="w-full flex flex-col justify-start items-start mb-4"> <div className="w-full flex flex-col justify-start items-start mb-4">
<div className="mb-2 text-base w-full flex flex-row justify-between items-center"> <div className="mb-2 text-base w-full flex flex-row justify-between items-center">
<span>Domain</span> <span className="dark:text-gray-400">Domain</span>
{domain !== "" && ( {domain !== "" && (
<a <a
className="text-sm flex flex-row justify-start items-center hover:underline hover:text-blue-600" className="text-sm flex flex-row justify-start items-center dark:text-gray-400 hover:underline hover:text-blue-600"
href={domain} href={domain}
target="_blank" target="_blank"
> >
<span className="mr-1">Go to my Slash</span> <span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" /> <Icon.ExternalLink className="w-4 h-auto" />
</a> </a>
)} )}
</div>
<div className="relative w-full">
<Input
className="w-full"
type="text"
placeholder="The domain of your Slash instance"
value={settingState.domain}
onChange={(e) => setPartialSettingState({ domain: e.target.value })}
/>
</div>
</div> </div>
<div className="relative w-full">
<div className="w-full flex flex-col justify-start items-start"> <Input
<span className="mb-2 text-base">Access Token</span> className="w-full"
<div className="relative w-full"> type="text"
<Input placeholder="The domain of your Slash instance"
className="w-full" value={settingState.domain}
type="text" onChange={(e) => setPartialSettingState({ domain: e.target.value })}
placeholder="The access token of your Slash instance" />
value={settingState.accessToken}
onChange={(e) => setPartialSettingState({ accessToken: e.target.value })}
/>
</div>
</div>
<div className="w-full mt-6">
<Button onClick={handleSaveSetting}>Save</Button>
</div> </div>
</div> </div>
{isInitialized && ( <div className="w-full flex flex-col justify-start items-start">
<> <span className="mb-2 text-base dark:text-gray-400">Access Token</span>
<Divider className="!my-6" /> <div className="relative w-full">
<Input
className="w-full"
type="text"
placeholder="The access token of your Slash instance"
value={settingState.accessToken}
onChange={(e) => setPartialSettingState({ accessToken: e.target.value })}
/>
</div>
</div>
<h2 className="flex flex-row justify-start items-center mb-4"> <div className="w-full mt-6 flex flex-row justify-end">
<span className="text-lg">Shortcuts</span> <Button onClick={handleSaveSetting}>Save</Button>
<span className="text-gray-500 mr-1">({shortcuts.length})</span> </div>
<PullShortcutsButton />
</h2> <Divider className="!my-6" />
<ShortcutsContainer />
</> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Preference</p>
)}
<div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center gap-x-1">
<span className="dark:text-gray-400">Color Theme</span>
</div>
<Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value)}>
{colorThemeOptions.map((option) => {
return (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
);
})}
</Select>
</div>
</div> </div>
</div>
<Toaster position="top-center" /> {isInitialized && (
</> <>
<Divider className="!my-6" />
<h2 className="flex flex-row justify-start items-center mb-4">
<span className="text-lg dark:text-gray-400">Shortcuts</span>
<span className="text-gray-500 mr-1">({shortcuts.length})</span>
<PullShortcutsButton />
</h2>
<ShortcutsContainer />
</>
)}
</div>
</div>
); );
}; };
export default IndexOptions; const Options = () => {
return (
<CssVarsProvider>
<IndexOptions />
<Toaster position="top-right" />
</CssVarsProvider>
);
};
export default Options;

View File

@ -1,5 +1,5 @@
import type { Shortcut } from "@/types/proto/api/v2/shortcut_service"; import type { Shortcut } from "@/types/proto/api/v2/shortcut_service";
import { Button, Divider, IconButton } from "@mui/joy"; import { Button, CssVarsProvider, Divider, IconButton } from "@mui/joy";
import { useStorage } from "@plasmohq/storage/hook"; import { useStorage } from "@plasmohq/storage/hook";
import { Toaster } from "react-hot-toast"; import { Toaster } from "react-hot-toast";
import CreateShortcutsButton from "@/components/CreateShortcutsButton"; import CreateShortcutsButton from "@/components/CreateShortcutsButton";
@ -7,9 +7,11 @@ import Icon from "@/components/Icon";
import Logo from "@/components/Logo"; import Logo from "@/components/Logo";
import PullShortcutsButton from "@/components/PullShortcutsButton"; import PullShortcutsButton from "@/components/PullShortcutsButton";
import ShortcutsContainer from "@/components/ShortcutsContainer"; import ShortcutsContainer from "@/components/ShortcutsContainer";
import useColorTheme from "./hooks/useColorTheme";
import "./style.css"; import "./style.css";
const IndexPopup = () => { const IndexPopup = () => {
useColorTheme();
const [domain] = useStorage<string>("domain", ""); const [domain] = useStorage<string>("domain", "");
const [accessToken] = useStorage<string>("access_token", ""); const [accessToken] = useStorage<string>("access_token", "");
const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []); const [shortcuts] = useStorage<Shortcut[]>("shortcuts", []);
@ -25,85 +27,84 @@ const IndexPopup = () => {
}; };
return ( return (
<> <div className="w-full min-w-[512px] px-4 pt-4">
<div className="w-full min-w-[512px] px-4 pt-4"> <div className="w-full flex flex-row justify-between items-center">
<div className="w-full flex flex-row justify-between items-center"> <div className="flex flex-row justify-start items-center dark:text-gray-400">
<div className="flex flex-row justify-start items-center"> <Logo className="w-6 h-auto mr-2" />
<Logo className="w-6 h-auto mr-2" /> <span className="">Slash</span>
<span className="">Slash</span> {isInitialized && (
{isInitialized && (
<>
<span className="mx-1 text-gray-400">/</span>
<span>Shortcuts</span>
<span className="text-gray-500 mr-0.5">({shortcuts.length})</span>
<PullShortcutsButton />
</>
)}
</div>
<div>{isInitialized && <CreateShortcutsButton />}</div>
</div>
<div className="w-full mt-4">
{isInitialized ? (
<> <>
{shortcuts.length !== 0 ? ( <span className="mx-1 text-gray-400">/</span>
<ShortcutsContainer /> <span>Shortcuts</span>
) : ( <span className="text-gray-500 mr-0.5">({shortcuts.length})</span>
<div className="w-full flex flex-col justify-center items-center"> <PullShortcutsButton />
<p>No shortcut found.</p>
</div>
)}
<Divider className="!mt-4 !mb-2 opacity-40" />
<div className="w-full flex flex-row justify-between items-center mb-2">
<div className="flex flex-row justify-start items-center">
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto text-gray-500" />
</IconButton>
<IconButton
size="sm"
variant="plain"
color="neutral"
component="a"
href="https://github.com/boojack/slash"
target="_blank"
>
<Icon.Github className="w-5 h-auto text-gray-500" />
</IconButton>
</div>
<div className="flex flex-row justify-end items-center">
<a
className="text-sm flex flex-row justify-start items-center text-gray-500 hover:underline hover:text-blue-600"
href={domain}
target="_blank"
>
<span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</div>
</div>
</> </>
) : (
<div className="w-full flex flex-col justify-start items-center">
<p>No domain and access token found.</p>
<div className="w-full flex flex-row justify-center items-center py-4">
<Button size="sm" color="primary" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto mr-1" /> Setting
</Button>
<span className="mx-2">Or</span>
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
</Button>
</div>
</div>
)} )}
</div> </div>
<div>{isInitialized && <CreateShortcutsButton />}</div>
</div> </div>
<Toaster position="top-right" /> <div className="w-full mt-4">
</> {isInitialized ? (
<>
{shortcuts.length !== 0 ? (
<ShortcutsContainer />
) : (
<div className="w-full flex flex-col justify-center items-center">
<p>No shortcut found.</p>
</div>
)}
<Divider className="!mt-4 !mb-2 opacity-40" />
<div className="w-full flex flex-row justify-between items-center mb-2">
<div className="flex flex-row justify-start items-center">
<IconButton size="sm" variant="plain" color="neutral" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto text-gray-500 dark:text-gray-400" />
</IconButton>
<IconButton size="sm" variant="plain" color="neutral" component="a" href="https://github.com/boojack/slash" target="_blank">
<Icon.Github className="w-5 h-auto text-gray-500 dark:text-gray-400" />
</IconButton>
</div>
<div className="flex flex-row justify-end items-center">
<a
className="text-sm flex flex-row justify-start items-center text-gray-500 dark:text-gray-400 hover:underline hover:text-blue-600"
href={domain}
target="_blank"
>
<span className="mr-1">Go to my Slash</span>
<Icon.ExternalLink className="w-4 h-auto" />
</a>
</div>
</div>
</>
) : (
<div className="w-full flex flex-col justify-start items-center">
<Icon.Cookie strokeWidth={1} className="w-20 h-auto mb-4 text-gray-400" />
<p className="dark:text-gray-400">Please set your domain and access token first.</p>
<div className="w-full flex flex-row justify-center items-center py-4">
<Button size="sm" color="primary" onClick={handleSettingButtonClick}>
<Icon.Settings className="w-5 h-auto mr-1" /> Setting
</Button>
<span className="mx-2 dark:text-gray-400">Or</span>
<Button size="sm" variant="outlined" color="neutral" onClick={handleRefreshButtonClick}>
<Icon.RefreshCcw className="w-5 h-auto mr-1" /> Refresh
</Button>
</div>
</div>
)}
</div>
</div>
); );
}; };
export default IndexPopup; const Popup = () => {
return (
<CssVarsProvider>
<IndexPopup />
<Toaster position="top-right" />
</CssVarsProvider>
);
};
export default Popup;

View File

@ -1,41 +0,0 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import { getUrlFavicon } from "../helpers/api";
interface FaviconState {
cache: {
[key: string]: string;
};
getOrFetchUrlFavicon: (url: string) => Promise<string>;
}
const useFaviconStore = create<FaviconState>()(
persist(
(set, get) => ({
cache: {},
getOrFetchUrlFavicon: async (url: string) => {
const cache = get().cache;
if (cache[url]) {
return cache[url];
}
try {
const { data: favicon } = await getUrlFavicon(url);
if (favicon) {
cache[url] = favicon;
set(cache);
return favicon;
}
} catch (error) {
// do nothing
}
return "";
},
}),
{
name: "favicon_cache",
}
)
);
export default useFaviconStore;

View File

@ -5,7 +5,7 @@
body, body,
html, html,
#root { #root {
@apply text-base; @apply text-base dark:bg-zinc-900;
font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei", font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "Noto Sans", "Noto Sans CJK SC", "Microsoft YaHei UI", "Microsoft YaHei",
"WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "WenQuanYi Micro Hei", sans-serif, "Segoe UI", Roboto, "Helvetica Neue", Arial, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol",
"Noto Color Emoji"; "Noto Color Emoji";

View File

@ -11,7 +11,14 @@
"language": "Language", "language": "Language",
"search": "Search", "search": "Search",
"email": "Email", "email": "Email",
"password": "Password" "password": "Password",
"account": "Account"
},
"auth": {
"sign-in": "Sign in",
"sign-up": "Sign up",
"sign-out": "Sign out",
"create-your-account": "Create your account"
}, },
"analytics": { "analytics": {
"self": "Analytics", "self": "Analytics",
@ -24,6 +31,7 @@
"operating-system": "Operating System" "operating-system": "Operating System"
}, },
"shortcut": { "shortcut": {
"visits": "{{count}} visits",
"visibility": { "visibility": {
"private": { "private": {
"self": "Private", "self": "Private",
@ -38,5 +46,37 @@
"description": "Visible to everyone on the internet" "description": "Visible to everyone on the internet"
} }
} }
},
"filter": {
"all": "All",
"mine": "Mine",
"compact-mode": "Compact mode",
"order-by": "Order by",
"direction": "Direction"
},
"user": {
"self": "User",
"nickname": "Nickname",
"email": "Email",
"role": "Role",
"profile": "Profile",
"action": {
"add-user": "Add user"
}
},
"settings": {
"self": "Setting",
"preference": {
"self": "Preference",
"color-theme": "Color theme"
},
"workspace": {
"self": "Workspace settings",
"custom-style": "Custom style",
"enable-user-signup": {
"self": "Enable user signup",
"description": "Once enabled, other users can signup."
}
}
} }
} }

View File

@ -11,7 +11,14 @@
"language": "语言", "language": "语言",
"search": "搜索", "search": "搜索",
"email": "邮箱", "email": "邮箱",
"password": "密码" "password": "密码",
"account": "账号"
},
"auth": {
"sign-in": "登录",
"sign-up": "注册",
"sign-out": "退出登录",
"create-your-account": "创建账号"
}, },
"analytics": { "analytics": {
"self": "分析", "self": "分析",
@ -24,6 +31,7 @@
"operating-system": "操作系统" "operating-system": "操作系统"
}, },
"shortcut": { "shortcut": {
"visits": "{{count}} 次访问",
"visibility": { "visibility": {
"private": { "private": {
"self": "私有的", "self": "私有的",
@ -38,5 +46,37 @@
"description": "对任何人可见" "description": "对任何人可见"
} }
} }
},
"filter": {
"all": "所有",
"mine": "我的",
"compact-mode": "紧凑模式",
"order-by": "排序方式",
"direction": "方向"
},
"user": {
"self": "用户",
"nickname": "昵称",
"email": "邮箱",
"role": "角色",
"profile": "账号",
"action": {
"add-user": "添加用户"
}
},
"settings": {
"self": "设置",
"preference": {
"self": "偏好设置",
"color-theme": "主题"
},
"workspace": {
"self": "系统设置",
"custom-style": "自定义样式",
"enable-user-signup": {
"self": "启用用户注册",
"description": "允许其他用户注册新账号"
}
}
} }
} }

View File

@ -11,7 +11,7 @@
"@emotion/react": "^11.11.1", "@emotion/react": "^11.11.1",
"@emotion/styled": "^11.11.0", "@emotion/styled": "^11.11.0",
"@mui/joy": "5.0.0-beta.7", "@mui/joy": "5.0.0-beta.7",
"@reduxjs/toolkit": "^1.9.5", "@reduxjs/toolkit": "^1.9.6",
"axios": "^0.27.2", "axios": "^0.27.2",
"classnames": "^2.3.2", "classnames": "^2.3.2",
"copy-to-clipboard": "^3.3.3", "copy-to-clipboard": "^3.3.3",
@ -34,18 +34,18 @@
"devDependencies": { "devDependencies": {
"@trivago/prettier-plugin-sort-imports": "^4.2.0", "@trivago/prettier-plugin-sort-imports": "^4.2.0",
"@types/lodash-es": "^4.17.9", "@types/lodash-es": "^4.17.9",
"@types/react": "^18.2.22", "@types/react": "^18.2.23",
"@types/react-dom": "^18.2.7", "@types/react-dom": "^18.2.8",
"@typescript-eslint/eslint-plugin": "^6.7.2", "@typescript-eslint/eslint-plugin": "^6.7.3",
"@typescript-eslint/parser": "^6.7.2", "@typescript-eslint/parser": "^6.7.3",
"@vitejs/plugin-react-swc": "^3.3.2", "@vitejs/plugin-react-swc": "^3.4.0",
"autoprefixer": "^10.4.16", "autoprefixer": "^10.4.16",
"eslint": "^8.50.0", "eslint": "^8.50.0",
"eslint-config-prettier": "^8.10.0", "eslint-config-prettier": "^8.10.0",
"eslint-plugin-prettier": "^4.2.1", "eslint-plugin-prettier": "^4.2.1",
"eslint-plugin-react": "^7.33.2", "eslint-plugin-react": "^7.33.2",
"long": "^5.2.3", "long": "^5.2.3",
"postcss": "^8.4.30", "postcss": "^8.4.31",
"prettier": "2.6.2", "prettier": "2.6.2",
"protobufjs": "^7.2.5", "protobufjs": "^7.2.5",
"typescript": "^5.2.2", "typescript": "^5.2.2",

File diff suppressed because it is too large Load Diff

View File

@ -1,15 +1,17 @@
import { Button, Divider, Input, Modal, ModalDialog, Radio, RadioGroup, Textarea } from "@mui/joy"; import { Button, Divider, Input, Modal, ModalDialog, Radio, RadioGroup, Textarea } from "@mui/joy";
import classnames from "classnames"; import classnames from "classnames";
import { isUndefined } from "lodash-es"; import { isUndefined, uniq } from "lodash-es";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useAppSelector } from "@/stores";
import useLoading from "../hooks/useLoading"; import useLoading from "../hooks/useLoading";
import { shortcutService } from "../services"; import { shortcutService } from "../services";
import Icon from "./Icon"; import Icon from "./Icon";
interface Props { interface Props {
shortcutId?: ShortcutId; shortcutId?: ShortcutId;
initialShortcut?: Partial<Shortcut>;
onClose: () => void; onClose: () => void;
onConfirm?: () => void; onConfirm?: () => void;
} }
@ -21,8 +23,9 @@ interface State {
const visibilities: Visibility[] = ["PRIVATE", "WORKSPACE", "PUBLIC"]; const visibilities: Visibility[] = ["PRIVATE", "WORKSPACE", "PUBLIC"];
const CreateShortcutDialog: React.FC<Props> = (props: Props) => { const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
const { onClose, onConfirm, shortcutId } = props; const { onClose, onConfirm, shortcutId, initialShortcut } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const { shortcutList } = useAppSelector((state) => state.shortcut);
const [state, setState] = useState<State>({ const [state, setState] = useState<State>({
shortcutCreate: { shortcutCreate: {
name: "", name: "",
@ -36,11 +39,13 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
description: "", description: "",
image: "", image: "",
}, },
...initialShortcut,
}, },
}); });
const [showAdditionalFields, setShowAdditionalFields] = useState<boolean>(false); const [showAdditionalFields, setShowAdditionalFields] = useState<boolean>(false);
const [showOpenGraphMetadata, setShowOpenGraphMetadata] = useState<boolean>(false); const [showOpenGraphMetadata, setShowOpenGraphMetadata] = useState<boolean>(false);
const [tag, setTag] = useState<string>(""); const [tag, setTag] = useState<string>("");
const tagSuggestions = uniq(shortcutList.map((shortcut) => shortcut.tags).flat());
const requestState = useLoading(false); const requestState = useLoading(false);
const isCreating = isUndefined(shortcutId); const isCreating = isUndefined(shortcutId);
@ -149,6 +154,14 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
}); });
}; };
const handleTagSuggestionsClick = (suggestion: string) => {
if (tag === "") {
setTag(suggestion);
} else {
setTag(`${tag} ${suggestion}`);
}
};
const handleSaveBtnClick = async () => { const handleSaveBtnClick = async () => {
if (!state.shortcutCreate.name) { if (!state.shortcutCreate.name) {
toast.error("Name is required"); toast.error("Name is required");
@ -220,6 +233,22 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
<div className="w-full flex flex-col justify-start items-start mb-3"> <div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">Tags</span> <span className="mb-2">Tags</span>
<Input className="w-full" type="text" placeholder="github slash" value={tag} onChange={handleTagsInputChange} /> <Input className="w-full" type="text" placeholder="github slash" value={tag} onChange={handleTagsInputChange} />
{tagSuggestions.length > 0 && (
<div className="w-full flex flex-row justify-start items-start mt-2">
<Icon.Asterisk className="w-4 h-auto shrink-0 mx-1 text-gray-400 dark:text-gray-600" />
<div className="w-auto flex flex-row justify-start items-start flex-wrap gap-x-2 gap-y-1">
{tagSuggestions.map((tag) => (
<span
className="text-gray-600 dark:text-gray-500 cursor-pointer max-w-[6rem] truncate block text-sm flex-nowrap leading-4 hover:text-black dark:hover:text-gray-400"
key={tag}
onClick={() => handleTagSuggestionsClick(tag)}
>
{tag}
</span>
))}
</div>
</div>
)}
</div> </div>
<div className="w-full flex flex-col justify-start items-start mb-3"> <div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">Visibility</span> <span className="mb-2">Visibility</span>
@ -283,10 +312,7 @@ const CreateShortcutDialog: React.FC<Props> = (props: Props) => {
)} )}
onClick={() => setShowOpenGraphMetadata(!showOpenGraphMetadata)} onClick={() => setShowOpenGraphMetadata(!showOpenGraphMetadata)}
> >
<span className="text-sm flex flex-row justify-start items-center"> <span className="text-sm flex flex-row justify-start items-center">Social media metadata</span>
Social media metadata
<Icon.Sparkles className="ml-1 w-4 h-auto text-blue-600" />
</span>
<button className="w-7 h-7 p-1 rounded-md"> <button className="w-7 h-7 p-1 rounded-md">
<Icon.ChevronDown className={classnames("w-4 h-auto text-gray-500", showOpenGraphMetadata ? "transform rotate-180" : "")} /> <Icon.ChevronDown className={classnames("w-4 h-auto text-gray-500", showOpenGraphMetadata ? "transform rotate-180" : "")} />
</button> </button>

View File

@ -66,11 +66,11 @@ const EditUserinfoDialog: React.FC<Props> = (props: Props) => {
</div> </div>
<div> <div>
<div className="w-full flex flex-col justify-start items-start mb-3"> <div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">Email</span> <span className="mb-2">{t("common.email")}</span>
<Input className="w-full" type="text" value={email} onChange={handleEmailChanged} /> <Input className="w-full" type="text" value={email} onChange={handleEmailChanged} />
</div> </div>
<div className="w-full flex flex-col justify-start items-start mb-3"> <div className="w-full flex flex-col justify-start items-start mb-3">
<span className="mb-2">Nickname</span> <span className="mb-2">{t("user.nickname")}</span>
<Input className="w-full" type="text" value={nickname} onChange={handleNicknameChanged} /> <Input className="w-full" type="text" value={nickname} onChange={handleNicknameChanged} />
</div> </div>
<div className="w-full flex flex-row justify-end items-center space-x-2"> <div className="w-full flex flex-row justify-end items-center space-x-2">

View File

@ -1,5 +1,6 @@
import { Avatar } from "@mui/joy"; import { Avatar } from "@mui/joy";
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import useWorkspaceStore from "@/stores/v1/workspace"; import useWorkspaceStore from "@/stores/v1/workspace";
import { PlanType } from "@/types/proto/api/v2/subscription_service"; import { PlanType } from "@/types/proto/api/v2/subscription_service";
@ -10,6 +11,7 @@ import Icon from "./Icon";
import Dropdown from "./common/Dropdown"; import Dropdown from "./common/Dropdown";
const Header: React.FC = () => { const Header: React.FC = () => {
const { t } = useTranslation();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const currentUser = useUserStore().getCurrentUser(); const currentUser = useUserStore().getCurrentUser();
const [showAboutDialog, setShowAboutDialog] = useState<boolean>(false); const [showAboutDialog, setShowAboutDialog] = useState<boolean>(false);
@ -52,27 +54,27 @@ const Header: React.FC = () => {
to="/setting/general" to="/setting/general"
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
> >
<Icon.User className="w-4 h-auto mr-2" /> Profile <Icon.User className="w-4 h-auto mr-2" /> {t("user.profile")}
</Link> </Link>
{isAdmin && ( {isAdmin && (
<Link <Link
to="/setting/workspace" to="/setting/workspace"
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
> >
<Icon.Settings className="w-4 h-auto mr-2" /> Setting <Icon.Settings className="w-4 h-auto mr-2" /> {t("settings.self")}
</Link> </Link>
)} )}
<button <button
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
onClick={() => setShowAboutDialog(true)} onClick={() => setShowAboutDialog(true)}
> >
<Icon.Info className="w-4 h-auto mr-2" /> About <Icon.Info className="w-4 h-auto mr-2" /> {t("common.about")}
</button> </button>
<button <button
className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60" className="w-full px-2 flex flex-row justify-start items-center text-left dark:text-gray-400 leading-8 cursor-pointer rounded hover:bg-gray-100 dark:hover:bg-zinc-800 disabled:cursor-not-allowed disabled:bg-gray-100 disabled:opacity-60"
onClick={() => handleSignOutButtonClick()} onClick={() => handleSignOutButtonClick()}
> >
<Icon.LogOut className="w-4 h-auto mr-2" /> Sign out <Icon.LogOut className="w-4 h-auto mr-2" /> {t("auth.sign-out")}
</button> </button>
</> </>
} }

View File

@ -1,9 +1,11 @@
import classNames from "classnames"; import classNames from "classnames";
import { useTranslation } from "react-i18next";
import { useAppSelector } from "../stores"; import { useAppSelector } from "../stores";
import useViewStore from "../stores/v1/view"; import useViewStore from "../stores/v1/view";
import Icon from "./Icon"; import Icon from "./Icon";
const Navigator = () => { const Navigator = () => {
const { t } = useTranslation();
const viewStore = useViewStore(); const viewStore = useViewStore();
const { shortcutList } = useAppSelector((state) => state.shortcut); const { shortcutList } = useAppSelector((state) => state.shortcut);
const tags = shortcutList.map((shortcut) => shortcut.tags).flat(); const tags = shortcutList.map((shortcut) => shortcut.tags).flat();
@ -22,7 +24,7 @@ const Navigator = () => {
onClick={() => viewStore.setFilter({ tab: "tab:all" })} onClick={() => viewStore.setFilter({ tab: "tab:all" })}
> >
<Icon.CircleSlash className="w-4 h-auto mr-1" /> <Icon.CircleSlash className="w-4 h-auto mr-1" />
<span className="font-normal">All</span> <span className="font-normal">{t("filter.all")}</span>
</button> </button>
<button <button
className={classNames( className={classNames(
@ -34,7 +36,7 @@ const Navigator = () => {
onClick={() => viewStore.setFilter({ tab: "tab:mine" })} onClick={() => viewStore.setFilter({ tab: "tab:mine" })}
> >
<Icon.User className="w-4 h-auto mr-1" /> <Icon.User className="w-4 h-auto mr-1" />
<span className="font-normal">Mine</span> <span className="font-normal">{t("filter.mine")}</span>
</button> </button>
{Array.from(sortedTagMap.keys()).map((tag) => ( {Array.from(sortedTagMap.keys()).map((tag) => (
<button <button

View File

@ -1,6 +1,6 @@
import { useState } from "react"; import { useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import useNavigateTo from "@/hooks/useNavigateTo";
import { shortcutService } from "../services"; import { shortcutService } from "../services";
import useUserStore from "../stores/v1/user"; import useUserStore from "../stores/v1/user";
import { showCommonDialog } from "./Alert"; import { showCommonDialog } from "./Alert";
@ -16,7 +16,7 @@ interface Props {
const ShortcutActionsDropdown = (props: Props) => { const ShortcutActionsDropdown = (props: Props) => {
const { shortcut } = props; const { shortcut } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigateTo = useNavigateTo();
const currentUser = useUserStore().getCurrentUser(); const currentUser = useUserStore().getCurrentUser();
const [showEditDialog, setShowEditDialog] = useState<boolean>(false); const [showEditDialog, setShowEditDialog] = useState<boolean>(false);
const [showQRCodeDialog, setShowQRCodeDialog] = useState<boolean>(false); const [showQRCodeDialog, setShowQRCodeDialog] = useState<boolean>(false);
@ -34,7 +34,7 @@ const ShortcutActionsDropdown = (props: Props) => {
}; };
const gotoAnalytics = () => { const gotoAnalytics = () => {
navigate(`/shortcut/${shortcut.id}#analytics`); navigateTo(`/shortcut/${shortcut.id}#analytics`);
}; };
return ( return (

View File

@ -1,12 +1,10 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import classNames from "classnames"; import classNames from "classnames";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { absolutifyLink } from "../helpers/utils"; import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils";
import useFaviconStore from "../stores/v1/favicon";
import useViewStore from "../stores/v1/view"; import useViewStore from "../stores/v1/view";
import Icon from "./Icon"; import Icon from "./Icon";
import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; import ShortcutActionsDropdown from "./ShortcutActionsDropdown";
@ -20,17 +18,8 @@ const ShortcutView = (props: Props) => {
const { shortcut } = props; const { shortcut } = props;
const { t } = useTranslation(); const { t } = useTranslation();
const viewStore = useViewStore(); const viewStore = useViewStore();
const faviconStore = useFaviconStore();
const [favicon, setFavicon] = useState<string | undefined>(undefined);
const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); const shortcutLink = absolutifyLink(`/s/${shortcut.name}`);
const favicon = getFaviconWithGoogleS2(shortcut.link);
useEffect(() => {
faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => {
if (url) {
setFavicon(url);
}
});
}, [shortcut.link]);
const handleCopyButtonClick = () => { const handleCopyButtonClick = () => {
copy(shortcutLink); copy(shortcutLink);
@ -63,9 +52,9 @@ const ShortcutView = (props: Props) => {
href={shortcutLink} href={shortcutLink}
> >
<div className="truncate"> <div className="truncate">
<span>{shortcut.title}</span> <span className="dark:text-gray-400">{shortcut.title}</span>
{shortcut.title ? ( {shortcut.title ? (
<span className="text-gray-400">(s/{shortcut.name})</span> <span className="text-gray-500">(s/{shortcut.name})</span>
) : ( ) : (
<> <>
<span className="text-gray-400 dark:text-gray-500">s/</span> <span className="text-gray-400 dark:text-gray-500">s/</span>
@ -113,16 +102,16 @@ const ShortcutView = (props: Props) => {
})} })}
{shortcut.tags.length === 0 && <span className="text-gray-400 text-sm font-mono leading-4 italic">No tags</span>} {shortcut.tags.length === 0 && <span className="text-gray-400 text-sm font-mono leading-4 italic">No tags</span>}
</div> </div>
<div className="w-full flex mt-2 gap-2"> <div className="w-full flex mt-2 gap-2 overflow-x-auto">
<Tooltip title="Creator" variant="solid" placement="top" arrow> <Tooltip title="Creator" variant="solid" placement="top" arrow>
<div className="w-auto px-2 leading-6 flex flex-row justify-start items-center border rounded-full text-gray-500 text-sm dark:border-zinc-800"> <div className="w-auto px-2 leading-6 flex flex-row justify-start items-center flex-nowra whitespace-nowrap border rounded-full text-gray-500 text-sm dark:border-zinc-800">
<Icon.User className="w-4 h-auto mr-1" /> <Icon.User className="w-4 h-auto mr-1" />
<span className="max-w-[4rem] sm:max-w-[6rem] truncate">{shortcut.creator.nickname}</span> <span className="max-w-[4rem] sm:max-w-[6rem] truncate">{shortcut.creator.nickname}</span>
</div> </div>
</Tooltip> </Tooltip>
<Tooltip title={t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)} variant="solid" placement="top" arrow> <Tooltip title={t(`shortcut.visibility.${shortcut.visibility.toLowerCase()}.description`)} variant="solid" placement="top" arrow>
<div <div
className="w-auto px-2 leading-6 flex flex-row justify-start items-center border rounded-full cursor-pointer text-gray-500 text-sm dark:border-zinc-800" className="w-auto px-2 leading-6 flex flex-row justify-start items-center flex-nowrap whitespace-nowrap border rounded-full cursor-pointer text-gray-500 text-sm dark:border-zinc-800"
onClick={() => viewStore.setFilter({ visibility: shortcut.visibility })} onClick={() => viewStore.setFilter({ visibility: shortcut.visibility })}
> >
<VisibilityIcon className="w-4 h-auto mr-1" visibility={shortcut.visibility} /> <VisibilityIcon className="w-4 h-auto mr-1" visibility={shortcut.visibility} />
@ -132,10 +121,10 @@ const ShortcutView = (props: Props) => {
<Tooltip title="View count" variant="solid" placement="top" arrow> <Tooltip title="View count" variant="solid" placement="top" arrow>
<Link <Link
to={`/shortcut/${shortcut.id}#analytics`} to={`/shortcut/${shortcut.id}#analytics`}
className="w-auto px-2 leading-6 flex flex-row justify-start items-center border rounded-full cursor-pointer text-gray-500 text-sm dark:border-zinc-800" className="w-auto px-2 leading-6 flex flex-row justify-start items-center flex-nowrap whitespace-nowrap border rounded-full cursor-pointer text-gray-500 text-sm dark:border-zinc-800"
> >
<Icon.BarChart2 className="w-4 h-auto mr-1" /> <Icon.BarChart2 className="w-4 h-auto mr-1" />
{shortcut.view} visits {t("shortcut.visits", { count: shortcut.view })}
</Link> </Link>
</Tooltip> </Tooltip>
</div> </div>

View File

@ -1,8 +1,6 @@
import classNames from "classnames"; import classNames from "classnames";
import { useEffect, useState } from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { absolutifyLink } from "../helpers/utils"; import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils";
import useFaviconStore from "../stores/v1/favicon";
import Icon from "./Icon"; import Icon from "./Icon";
import ShortcutActionsDropdown from "./ShortcutActionsDropdown"; import ShortcutActionsDropdown from "./ShortcutActionsDropdown";
@ -12,17 +10,8 @@ interface Props {
const ShortcutView = (props: Props) => { const ShortcutView = (props: Props) => {
const { shortcut } = props; const { shortcut } = props;
const faviconStore = useFaviconStore();
const [favicon, setFavicon] = useState<string | undefined>(undefined);
const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); const shortcutLink = absolutifyLink(`/s/${shortcut.name}`);
const favicon = getFaviconWithGoogleS2(shortcut.link);
useEffect(() => {
faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => {
if (url) {
setFavicon(url);
}
});
}, [shortcut.link]);
return ( return (
<> <>
@ -50,9 +39,9 @@ const ShortcutView = (props: Props) => {
target="_blank" target="_blank"
> >
<div className="truncate"> <div className="truncate">
<span>{shortcut.title}</span> <span className="dark:text-gray-400">{shortcut.title}</span>
{shortcut.title ? ( {shortcut.title ? (
<span className="text-gray-400">(s/{shortcut.name})</span> <span className="text-gray-500">(s/{shortcut.name})</span>
) : ( ) : (
<> <>
<span className="text-gray-400 dark:text-gray-500">s/</span> <span className="text-gray-400 dark:text-gray-500">s/</span>

View File

@ -16,8 +16,8 @@ const ShortcutsContainer: React.FC<Props> = (props: Props) => {
return ( return (
<div <div
className={classNames( className={classNames(
"w-full grid grid-cols-1 gap-y-2 sm:gap-2", "w-full grid grid-cols-1 gap-3 sm:gap-4",
displayStyle === "full" ? "sm:grid-cols-2" : "grid-cols-2 sm:grid-cols-4 gap-2" displayStyle === "full" ? "sm:grid-cols-2 lg:grid-cols-3" : "grid-cols-2 sm:grid-cols-4"
)} )}
> >
{shortcutList.map((shortcut) => { {shortcutList.map((shortcut) => {

View File

@ -1,9 +1,11 @@
import { Divider, Option, Select, Switch } from "@mui/joy"; import { Divider, Option, Select, Switch } from "@mui/joy";
import { useTranslation } from "react-i18next";
import useViewStore from "../stores/v1/view"; import useViewStore from "../stores/v1/view";
import Icon from "./Icon"; import Icon from "./Icon";
import Dropdown from "./common/Dropdown"; import Dropdown from "./common/Dropdown";
const ViewSetting = () => { const ViewSetting = () => {
const { t } = useTranslation();
const viewStore = useViewStore(); const viewStore = useViewStore();
const order = viewStore.getOrder(); const order = viewStore.getOrder();
const { field, direction } = order; const { field, direction } = order;
@ -20,7 +22,7 @@ const ViewSetting = () => {
actions={ actions={
<div className="w-52 p-2 gap-2 flex flex-col justify-start items-start" onClick={(e) => e.stopPropagation()}> <div className="w-52 p-2 gap-2 flex flex-col justify-start items-start" onClick={(e) => e.stopPropagation()}>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="text-sm shrink-0 mr-2">Compact mode</span> <span className="text-sm shrink-0 mr-2">{t("filter.compact-mode")}</span>
<Switch <Switch
size="sm" size="sm"
checked={displayStyle === "compact"} checked={displayStyle === "compact"}
@ -29,7 +31,7 @@ const ViewSetting = () => {
</div> </div>
<Divider className="!my-1" /> <Divider className="!my-1" />
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="text-sm shrink-0 mr-2">Order by</span> <span className="text-sm shrink-0 mr-2">{t("filter.order-by")}</span>
<Select size="sm" value={field} onChange={(_, value) => viewStore.setOrder({ field: value as any })}> <Select size="sm" value={field} onChange={(_, value) => viewStore.setOrder({ field: value as any })}>
<Option value={"name"}>Name</Option> <Option value={"name"}>Name</Option>
<Option value={"updatedTs"}>CreatedAt</Option> <Option value={"updatedTs"}>CreatedAt</Option>
@ -38,7 +40,7 @@ const ViewSetting = () => {
</Select> </Select>
</div> </div>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<span className="text-sm shrink-0 mr-2">Direction</span> <span className="text-sm shrink-0 mr-2">{t("filter.direction")}</span>
<Select size="sm" value={direction} onChange={(_, value) => viewStore.setOrder({ direction: value as any })}> <Select size="sm" value={direction} onChange={(_, value) => viewStore.setOrder({ direction: value as any })}>
<Option value={"asc"}>ASC</Option> <Option value={"asc"}>ASC</Option>
<Option value={"desc"}>DESC</Option> <Option value={"desc"}>DESC</Option>

View File

@ -1,5 +1,4 @@
import { ReactNode, useEffect, useRef } from "react"; import { ReactNode, useEffect, useRef, useState } from "react";
import useToggle from "../../hooks/useToggle";
import Icon from "../Icon"; import Icon from "../Icon";
interface Props { interface Props {
@ -11,14 +10,14 @@ interface Props {
const Dropdown: React.FC<Props> = (props: Props) => { const Dropdown: React.FC<Props> = (props: Props) => {
const { trigger, actions, className, actionsClassName } = props; const { trigger, actions, className, actionsClassName } = props;
const [dropdownStatus, toggleDropdownStatus] = useToggle(false); const [dropdownStatus, setDropdownStatus] = useState(false);
const dropdownWrapperRef = useRef<HTMLDivElement>(null); const dropdownWrapperRef = useRef<HTMLDivElement>(null);
useEffect(() => { useEffect(() => {
if (dropdownStatus) { if (dropdownStatus) {
const handleClickOutside = (event: MouseEvent) => { const handleClickOutside = (event: MouseEvent) => {
if (!dropdownWrapperRef.current?.contains(event.target as Node)) { if (!dropdownWrapperRef.current?.contains(event.target as Node)) {
toggleDropdownStatus(false); setDropdownStatus(false);
} }
}; };
@ -35,7 +34,7 @@ const Dropdown: React.FC<Props> = (props: Props) => {
const handleToggleDropdownStatus = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => { const handleToggleDropdownStatus = (e: React.MouseEvent<HTMLDivElement, MouseEvent>) => {
e.stopPropagation(); e.stopPropagation();
toggleDropdownStatus(); setDropdownStatus(!dropdownStatus);
}; };
return ( return (

View File

@ -15,7 +15,7 @@ const AccountSection: React.FC = () => {
return ( return (
<> <>
<div className="w-full flex flex-col justify-start items-start gap-y-2"> <div className="w-full flex flex-col justify-start items-start gap-y-2">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Account</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("common.account")}</p>
<p className="flex flex-row justify-start items-center mt-2 dark:text-gray-400"> <p className="flex flex-row justify-start items-center mt-2 dark:text-gray-400">
<span className="text-xl">{currentUser.nickname}</span> <span className="text-xl">{currentUser.nickname}</span>
{isAdmin && <span className="ml-2 bg-blue-600 text-white px-2 leading-6 text-sm rounded-full">Admin</span>} {isAdmin && <span className="ml-2 bg-blue-600 text-white px-2 leading-6 text-sm rounded-full">Admin</span>}

View File

@ -1,12 +1,14 @@
import { Button, IconButton } from "@mui/joy"; import { Button, IconButton } from "@mui/joy";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import useUserStore from "../../stores/v1/user"; import useUserStore from "../../stores/v1/user";
import { showCommonDialog } from "../Alert"; import { showCommonDialog } from "../Alert";
import CreateUserDialog from "../CreateUserDialog"; import CreateUserDialog from "../CreateUserDialog";
import Icon from "../Icon"; import Icon from "../Icon";
const MemberSection = () => { const MemberSection = () => {
const { t } = useTranslation();
const userStore = useUserStore(); const userStore = useUserStore();
const [showCreateUserDialog, setShowCreateUserDialog] = useState<boolean>(false); const [showCreateUserDialog, setShowCreateUserDialog] = useState<boolean>(false);
const [currentEditingUser, setCurrentEditingUser] = useState<User | undefined>(undefined); const [currentEditingUser, setCurrentEditingUser] = useState<User | undefined>(undefined);
@ -43,7 +45,7 @@ const MemberSection = () => {
<div className="w-full"> <div className="w-full">
<div className="sm:flex sm:items-center"> <div className="sm:flex sm:items-center">
<div className="sm:flex-auto"> <div className="sm:flex-auto">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Users</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("user.self")}</p>
<p className="mt-2 text-sm text-gray-700 dark:text-gray-600"> <p className="mt-2 text-sm text-gray-700 dark:text-gray-600">
A list of all the users in your workspace including their nickname, email and role. A list of all the users in your workspace including their nickname, email and role.
</p> </p>
@ -57,7 +59,7 @@ const MemberSection = () => {
setCurrentEditingUser(undefined); setCurrentEditingUser(undefined);
}} }}
> >
Add user {t("user.action.add-user")}
</Button> </Button>
</div> </div>
</div> </div>
@ -68,20 +70,20 @@ const MemberSection = () => {
<thead> <thead>
<tr> <tr>
<th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-500"> <th scope="col" className="py-3.5 pl-4 pr-3 text-left text-sm font-semibold text-gray-900 dark:text-gray-500">
Nickname {t("user.nickname")}
</th> </th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500"> <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500">
Email {t("user.email")}
</th> </th>
<th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500"> <th scope="col" className="px-3 py-3.5 text-left text-sm font-semibold text-gray-900 dark:text-gray-500">
Role {t("user.role")}
</th> </th>
<th scope="col" className="relative py-3.5 pl-3 pr-4"> <th scope="col" className="relative py-3.5 pl-3 pr-4">
<span className="sr-only">Edit</span> <span className="sr-only">{t("common.edit")}</span>
</th> </th>
</tr> </tr>
</thead> </thead>
<tbody className="divide-y divide-gray-200"> <tbody className="divide-y divide-gray-200 dark:divide-zinc-800">
{userList.map((user) => ( {userList.map((user) => (
<tr key={user.email}> <tr key={user.email}>
<td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-500">{user.nickname}</td> <td className="whitespace-nowrap py-4 pl-4 pr-3 text-sm text-gray-900 dark:text-gray-500">{user.nickname}</td>

View File

@ -1,6 +1,5 @@
import { Option, Select } from "@mui/joy"; import { Option, Select } from "@mui/joy";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { releaseGuard } from "@/helpers/utils";
import { UserSetting, UserSetting_ColorTheme, UserSetting_Locale } from "@/types/proto/api/v2/user_setting_service"; import { UserSetting, UserSetting_ColorTheme, UserSetting_Locale } from "@/types/proto/api/v2/user_setting_service";
import useUserStore from "../../stores/v1/user"; import useUserStore from "../../stores/v1/user";
import BetaBadge from "../BetaBadge"; import BetaBadge from "../BetaBadge";
@ -61,10 +60,10 @@ const PreferenceSection: React.FC = () => {
return ( return (
<> <>
<div className="w-full flex flex-col justify-start items-start gap-y-2"> <div className="w-full flex flex-col justify-start items-start gap-y-2">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Preference</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("settings.preference.self")}</p>
<div className="w-full flex flex-row justify-between items-center"> <div className="w-full flex flex-row justify-between items-center">
<div className="flex flex-row justify-start items-center gap-x-1"> <div className="flex flex-row justify-start items-center gap-x-1">
<span className="dark:text-gray-400">Color Theme</span> <span className="dark:text-gray-400">{t("settings.preference.color-theme")}</span>
</div> </div>
<Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value as UserSetting_ColorTheme)}> <Select defaultValue={colorTheme} onChange={(_, value) => handleSelectColorTheme(value as UserSetting_ColorTheme)}>
{colorThemeOptions.map((option) => { {colorThemeOptions.map((option) => {
@ -76,23 +75,21 @@ const PreferenceSection: React.FC = () => {
})} })}
</Select> </Select>
</div> </div>
{releaseGuard() && ( <div className="w-full flex flex-row justify-between items-center">
<div className="w-full flex flex-row justify-between items-center"> <div className="flex flex-row justify-start items-center gap-x-1">
<div className="flex flex-row justify-start items-center gap-x-1"> <span className="dark:text-gray-400">{t("common.language")}</span>
<span className="dark:text-gray-400">{t("common.language")}</span> <BetaBadge />
<BetaBadge />
</div>
<Select defaultValue={language} onChange={(_, value) => handleSelectLanguage(value as UserSetting_Locale)}>
{languageOptions.map((option) => {
return (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
);
})}
</Select>
</div> </div>
)} <Select defaultValue={language} onChange={(_, value) => handleSelectLanguage(value as UserSetting_Locale)}>
{languageOptions.map((option) => {
return (
<Option key={option.value} value={option.value}>
{option.label}
</Option>
);
})}
</Select>
</div>
</div> </div>
</> </>
); );

View File

@ -2,11 +2,13 @@ import { Button, Checkbox, Textarea } from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { useRef, useState } from "react"; import { useRef, useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next";
import { workspaceServiceClient } from "@/grpcweb"; import { workspaceServiceClient } from "@/grpcweb";
import useWorkspaceStore from "@/stores/v1/workspace"; import useWorkspaceStore from "@/stores/v1/workspace";
import { WorkspaceSetting } from "@/types/proto/api/v2/workspace_service"; import { WorkspaceSetting } from "@/types/proto/api/v2/workspace_service";
const WorkspaceSection: React.FC = () => { const WorkspaceSection: React.FC = () => {
const { t } = useTranslation();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const [workspaceSetting, setWorkspaceSetting] = useState<WorkspaceSetting>(workspaceStore.setting); const [workspaceSetting, setWorkspaceSetting] = useState<WorkspaceSetting>(workspaceStore.setting);
const originalWorkspaceSetting = useRef<WorkspaceSetting>(workspaceStore.setting); const originalWorkspaceSetting = useRef<WorkspaceSetting>(workspaceStore.setting);
@ -56,9 +58,9 @@ const WorkspaceSection: React.FC = () => {
return ( return (
<div className="w-full flex flex-col justify-start items-start space-y-4"> <div className="w-full flex flex-col justify-start items-start space-y-4">
<p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">Workspace settings</p> <p className="text-base font-semibold leading-6 text-gray-900 dark:text-gray-500">{t("settings.workspace.self")}</p>
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<p className="mt-2 dark:text-gray-400">Custom style</p> <p className="mt-2 dark:text-gray-400">{t("settings.workspace.custom-style")}</p>
<Textarea <Textarea
className="w-full mt-2" className="w-full mt-2"
minRows={2} minRows={2}
@ -69,15 +71,15 @@ const WorkspaceSection: React.FC = () => {
</div> </div>
<div className="w-full flex flex-col justify-start items-start"> <div className="w-full flex flex-col justify-start items-start">
<Checkbox <Checkbox
label="Enable user signup" label={t("settings.workspace.enable-user-signup.self")}
checked={workspaceSetting.enableSignup} checked={workspaceSetting.enableSignup}
onChange={(event) => handleEnableSignUpChange(event.target.checked)} onChange={(event) => handleEnableSignUpChange(event.target.checked)}
/> />
<p className="mt-2 text-gray-500">Once enabled, other users can signup.</p> <p className="mt-2 text-gray-500">{t("settings.workspace.enable-user-signup.description")}</p>
</div> </div>
<div> <div>
<Button variant="outlined" color="neutral" disabled={!allowSave} onClick={handleSaveWorkspaceSetting}> <Button variant="outlined" color="neutral" disabled={!allowSave} onClick={handleSaveWorkspaceSetting}>
Save {t("common.save")}
</Button> </Button>
</div> </div>
</div> </div>

View File

@ -71,7 +71,3 @@ export function patchShortcut(shortcutPatch: ShortcutPatch) {
export function deleteShortcutById(shortcutId: ShortcutId) { export function deleteShortcutById(shortcutId: ShortcutId) {
return axios.delete(`/api/v1/shortcut/${shortcutId}`); return axios.delete(`/api/v1/shortcut/${shortcutId}`);
} }
export function getUrlFavicon(url: string) {
return axios.get<string>(`/api/v1/url/favicon?url=${url}`);
}

View File

@ -13,3 +13,12 @@ export const absolutifyLink = (rel: string): string => {
export const releaseGuard = () => { export const releaseGuard = () => {
return import.meta.env.MODE === "development"; return import.meta.env.MODE === "development";
}; };
export const getFaviconWithGoogleS2 = (url: string) => {
try {
const urlObject = new URL(url);
return `https://www.google.com/s2/favicons?sz=128&domain=${urlObject.hostname}`;
} catch (error) {
return undefined;
}
};

View File

@ -0,0 +1,20 @@
import { NavigateOptions, useNavigate } from "react-router-dom";
const useNavigateTo = () => {
const navigateTo = useNavigate();
const navigateToWithViewTransition = (to: string, options?: NavigateOptions) => {
const document = window.document as any;
if (!document.startViewTransition) {
navigateTo(to, options);
} else {
document.startViewTransition(() => {
navigateTo(to, options);
});
}
};
return navigateToWithViewTransition;
};
export default useNavigateTo;

View File

@ -1,21 +0,0 @@
import { useCallback, useState } from "react";
// Parameter is the boolean, with default "false" value
const useToggle = (initialState = false): [boolean, (nextState?: boolean) => void] => {
// Initialize the state
const [state, setState] = useState(initialState);
// Define and memorize toggler function in case we pass down the comopnent,
// This function change the boolean value to it's opposite value
const toggle = useCallback((nextState?: boolean) => {
if (nextState !== undefined) {
setState(nextState);
} else {
setState((state) => !state);
}
}, []);
return [state, toggle];
};
export default useToggle;

View File

@ -2,13 +2,14 @@ import { useColorScheme } from "@mui/joy";
import { isEqual } from "lodash-es"; import { isEqual } from "lodash-es";
import { useEffect } from "react"; import { useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Outlet, useNavigate } from "react-router-dom"; import { Outlet } from "react-router-dom";
import { UserSetting_ColorTheme } from "@/types/proto/api/v2/user_setting_service"; import useNavigateTo from "@/hooks/useNavigateTo";
import { UserSetting_ColorTheme, UserSetting_Locale } from "@/types/proto/api/v2/user_setting_service";
import Header from "../components/Header"; import Header from "../components/Header";
import useUserStore from "../stores/v1/user"; import useUserStore from "../stores/v1/user";
const Root: React.FC = () => { const Root: React.FC = () => {
const navigate = useNavigate(); const navigateTo = useNavigateTo();
const { setMode } = useColorScheme(); const { setMode } = useColorScheme();
const { i18n } = useTranslation(); const { i18n } = useTranslation();
const userStore = useUserStore(); const userStore = useUserStore();
@ -18,7 +19,7 @@ const Root: React.FC = () => {
useEffect(() => { useEffect(() => {
if (!currentUser) { if (!currentUser) {
navigate("/auth", { navigateTo("/auth", {
replace: true, replace: true,
}); });
return; return;
@ -33,7 +34,7 @@ const Root: React.FC = () => {
return; return;
} }
if (isEqual(currentUserSetting.locale, "LOCALE_ZH")) { if (isEqual(currentUserSetting.locale, UserSetting_Locale.LOCALE_ZH)) {
i18n.changeLanguage("zh"); i18n.changeLanguage("zh");
} else { } else {
i18n.changeLanguage("en"); i18n.changeLanguage("en");

View File

@ -69,9 +69,9 @@ const Home: React.FC = () => {
</div> </div>
<FilterView /> <FilterView />
{loadingState.isLoading ? ( {loadingState.isLoading ? (
<div className="py-12 w-full flex flex-row justify-center items-center opacity-80"> <div className="py-12 w-full flex flex-row justify-center items-center opacity-80 dark:text-gray-500">
<Icon.Loader className="mr-2 w-5 h-auto animate-spin" /> <Icon.Loader className="mr-2 w-5 h-auto animate-spin" />
loading {t("common.loading")}
</div> </div>
) : orderedShortcutList.length === 0 ? ( ) : orderedShortcutList.length === 0 ? (
<div className="py-16 w-full flex flex-col justify-center items-center text-gray-400"> <div className="py-16 w-full flex flex-col justify-center items-center text-gray-400">

View File

@ -0,0 +1,62 @@
import { Button } from "@mui/joy";
import { useEffect, useState } from "react";
import { useLocation } from "react-router-dom";
import CreateShortcutDialog from "@/components/CreateShortcutDialog";
import Icon from "@/components/Icon";
import useNavigateTo from "@/hooks/useNavigateTo";
import useUserStore from "@/stores/v1/user";
interface State {
showCreateShortcutButton: boolean;
}
const NotFound = () => {
const location = useLocation();
const navigateTo = useNavigateTo();
const currentUser = useUserStore().getCurrentUser();
const [state, setState] = useState<State>({
showCreateShortcutButton: false,
});
const [showCreateShortcutDialog, setShowCreateShortcutDialog] = useState(false);
const params = new URLSearchParams(location.search);
useEffect(() => {
const shortcut = params.get("shortcut");
if (currentUser && shortcut) {
setState({
...state,
showCreateShortcutButton: true,
});
}
}, []);
return (
<>
<div className="w-full h-full overflow-y-auto overflow-x-hidden bg-zinc-100 dark:bg-zinc-800">
<div className="w-full h-full flex flex-col justify-center items-center">
<Icon.Meh strokeWidth={1} className="w-20 h-auto opacity-80 dark:text-gray-300" />
<p className="mt-4 mb-8 text-4xl font-mono dark:text-gray-300">404</p>
{state.showCreateShortcutButton && (
<Button
variant="outlined"
startDecorator={<Icon.Plus className="w-5 h-auto" />}
onClick={() => setShowCreateShortcutDialog(true)}
>
Create shortcut
</Button>
)}
</div>
</div>
{showCreateShortcutDialog && (
<CreateShortcutDialog
initialShortcut={{ name: params.get("shortcut") || "" }}
onClose={() => setShowCreateShortcutDialog(false)}
onConfirm={() => navigateTo("/")}
/>
)}
</>
);
};
export default NotFound;

View File

@ -1,10 +1,11 @@
import { Tooltip } from "@mui/joy"; import { Tooltip } from "@mui/joy";
import classNames from "classnames"; import classNames from "classnames";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import { useEffect, useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useLoaderData, useNavigate } from "react-router-dom"; import { useLoaderData } from "react-router-dom";
import useNavigateTo from "@/hooks/useNavigateTo";
import { showCommonDialog } from "../components/Alert"; import { showCommonDialog } from "../components/Alert";
import AnalyticsView from "../components/AnalyticsView"; import AnalyticsView from "../components/AnalyticsView";
import CreateShortcutDialog from "../components/CreateShortcutDialog"; import CreateShortcutDialog from "../components/CreateShortcutDialog";
@ -12,9 +13,8 @@ import GenerateQRCodeDialog from "../components/GenerateQRCodeDialog";
import Icon from "../components/Icon"; import Icon from "../components/Icon";
import VisibilityIcon from "../components/VisibilityIcon"; import VisibilityIcon from "../components/VisibilityIcon";
import Dropdown from "../components/common/Dropdown"; import Dropdown from "../components/common/Dropdown";
import { absolutifyLink } from "../helpers/utils"; import { absolutifyLink, getFaviconWithGoogleS2 } from "../helpers/utils";
import { shortcutService } from "../services"; import { shortcutService } from "../services";
import useFaviconStore from "../stores/v1/favicon";
import useUserStore from "../stores/v1/user"; import useUserStore from "../stores/v1/user";
interface State { interface State {
@ -23,26 +23,17 @@ interface State {
const ShortcutDetail = () => { const ShortcutDetail = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigateTo = useNavigateTo();
const shortcutId = (useLoaderData() as Shortcut).id; const shortcutId = (useLoaderData() as Shortcut).id;
const shortcut = shortcutService.getShortcutById(shortcutId) as Shortcut; const shortcut = shortcutService.getShortcutById(shortcutId) as Shortcut;
const currentUser = useUserStore().getCurrentUser(); const currentUser = useUserStore().getCurrentUser();
const faviconStore = useFaviconStore();
const [state, setState] = useState<State>({ const [state, setState] = useState<State>({
showEditModal: false, showEditModal: false,
}); });
const [favicon, setFavicon] = useState<string | undefined>(undefined);
const [showQRCodeDialog, setShowQRCodeDialog] = useState<boolean>(false); const [showQRCodeDialog, setShowQRCodeDialog] = useState<boolean>(false);
const havePermission = currentUser.role === "ADMIN" || shortcut.creatorId === currentUser.id; const havePermission = currentUser.role === "ADMIN" || shortcut.creatorId === currentUser.id;
const shortcutLink = absolutifyLink(`/s/${shortcut.name}`); const shortcutLink = absolutifyLink(`/s/${shortcut.name}`);
const favicon = getFaviconWithGoogleS2(shortcut.link);
useEffect(() => {
faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => {
if (url) {
setFavicon(url);
}
});
}, [shortcut.link]);
const handleCopyButtonClick = () => { const handleCopyButtonClick = () => {
copy(shortcutLink); copy(shortcutLink);
@ -56,7 +47,7 @@ const ShortcutDetail = () => {
style: "danger", style: "danger",
onConfirm: async () => { onConfirm: async () => {
await shortcutService.deleteShortcutById(shortcut.id); await shortcutService.deleteShortcutById(shortcut.id);
navigate("/", { navigateTo("/", {
replace: true, replace: true,
}); });
}, },

View File

@ -2,7 +2,8 @@ import { Button, Input } from "@mui/joy";
import React, { FormEvent, useEffect, useState } from "react"; import React, { FormEvent, useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom"; import { Link } from "react-router-dom";
import useNavigateTo from "@/hooks/useNavigateTo";
import useWorkspaceStore from "@/stores/v1/workspace"; import useWorkspaceStore from "@/stores/v1/workspace";
import * as api from "../helpers/api"; import * as api from "../helpers/api";
import useLoading from "../hooks/useLoading"; import useLoading from "../hooks/useLoading";
@ -10,7 +11,7 @@ import useUserStore from "../stores/v1/user";
const SignIn: React.FC = () => { const SignIn: React.FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigateTo = useNavigateTo();
const userStore = useUserStore(); const userStore = useUserStore();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
@ -20,7 +21,7 @@ const SignIn: React.FC = () => {
useEffect(() => { useEffect(() => {
if (userStore.getCurrentUser()) { if (userStore.getCurrentUser()) {
return navigate("/", { return navigateTo("/", {
replace: true, replace: true,
}); });
} }
@ -52,7 +53,7 @@ const SignIn: React.FC = () => {
await api.signin(email, password); await api.signin(email, password);
const user = await userStore.fetchCurrentUser(); const user = await userStore.fetchCurrentUser();
if (user) { if (user) {
navigate("/", { navigateTo("/", {
replace: true, replace: true,
}); });
} else { } else {
@ -99,15 +100,15 @@ const SignIn: React.FC = () => {
disabled={actionBtnLoadingState.isLoading || !allowConfirm} disabled={actionBtnLoadingState.isLoading || !allowConfirm}
onClick={handleSigninBtnClick} onClick={handleSigninBtnClick}
> >
Sign in {t("auth.sign-in")}
</Button> </Button>
</div> </div>
</form> </form>
{workspaceStore.setting.enableSignup && ( {workspaceStore.profile.enableSignup && (
<p className="w-full mt-4 text-sm"> <p className="w-full mt-4 text-sm">
<span className="dark:text-gray-500">{"Don't have an account yet?"}</span> <span className="dark:text-gray-500">{"Don't have an account yet?"}</span>
<Link to="/auth/signup" className="cursor-pointer ml-2 text-blue-600 hover:underline"> <Link to="/auth/signup" className="cursor-pointer ml-2 text-blue-600 hover:underline">
Sign up {t("auth.sign-up")}
</Link> </Link>
</p> </p>
)} )}

View File

@ -2,7 +2,8 @@ import { Button, Input } from "@mui/joy";
import React, { FormEvent, useEffect, useState } from "react"; import React, { FormEvent, useEffect, useState } from "react";
import { toast } from "react-hot-toast"; import { toast } from "react-hot-toast";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Link, useNavigate } from "react-router-dom"; import { Link } from "react-router-dom";
import useNavigateTo from "@/hooks/useNavigateTo";
import useWorkspaceStore from "@/stores/v1/workspace"; import useWorkspaceStore from "@/stores/v1/workspace";
import * as api from "../helpers/api"; import * as api from "../helpers/api";
import useLoading from "../hooks/useLoading"; import useLoading from "../hooks/useLoading";
@ -10,7 +11,7 @@ import useUserStore from "../stores/v1/user";
const SignUp: React.FC = () => { const SignUp: React.FC = () => {
const { t } = useTranslation(); const { t } = useTranslation();
const navigate = useNavigate(); const navigateTo = useNavigateTo();
const userStore = useUserStore(); const userStore = useUserStore();
const workspaceStore = useWorkspaceStore(); const workspaceStore = useWorkspaceStore();
const [email, setEmail] = useState(""); const [email, setEmail] = useState("");
@ -21,13 +22,13 @@ const SignUp: React.FC = () => {
useEffect(() => { useEffect(() => {
if (userStore.getCurrentUser()) { if (userStore.getCurrentUser()) {
return navigate("/", { return navigateTo("/", {
replace: true, replace: true,
}); });
} }
if (!workspaceStore.setting.enableSignup) { if (!workspaceStore.profile.enableSignup) {
return navigate("/auth", { return navigateTo("/auth", {
replace: true, replace: true,
}); });
} }
@ -59,7 +60,7 @@ const SignUp: React.FC = () => {
await api.signup(email, nickname, password); await api.signup(email, nickname, password);
const user = await userStore.fetchCurrentUser(); const user = await userStore.fetchCurrentUser();
if (user) { if (user) {
navigate("/", { navigateTo("/", {
replace: true, replace: true,
}); });
} else { } else {
@ -80,7 +81,7 @@ const SignUp: React.FC = () => {
<img id="logo-img" src="/logo.png" className="w-12 h-auto mr-2 -mt-1" alt="logo" /> <img id="logo-img" src="/logo.png" className="w-12 h-auto mr-2 -mt-1" alt="logo" />
<span className="text-3xl opacity-80 dark:text-gray-500">Slash</span> <span className="text-3xl opacity-80 dark:text-gray-500">Slash</span>
</div> </div>
<p className="w-full text-2xl mt-6 dark:text-gray-500">Create your account</p> <p className="w-full text-2xl mt-6 dark:text-gray-500">{t("auth.create-your-account")}</p>
<form className="w-full mt-4" onSubmit={handleSignupBtnClick}> <form className="w-full mt-4" onSubmit={handleSignupBtnClick}>
<div className={`flex flex-col justify-start items-start w-full ${actionBtnLoadingState.isLoading ? "opacity-80" : ""}`}> <div className={`flex flex-col justify-start items-start w-full ${actionBtnLoadingState.isLoading ? "opacity-80" : ""}`}>
<div className="w-full flex flex-col mb-2"> <div className="w-full flex flex-col mb-2">
@ -111,14 +112,14 @@ const SignUp: React.FC = () => {
disabled={actionBtnLoadingState.isLoading || !allowConfirm} disabled={actionBtnLoadingState.isLoading || !allowConfirm}
onClick={handleSignupBtnClick} onClick={handleSignupBtnClick}
> >
Sign up {t("auth.sign-up")}
</Button> </Button>
</div> </div>
</form> </form>
<p className="w-full mt-4 text-sm"> <p className="w-full mt-4 text-sm">
<span className="dark:text-gray-500">{"Already has an account?"}</span> <span className="dark:text-gray-500">{"Already has an account?"}</span>
<Link to="/auth" className="cursor-pointer ml-2 text-blue-600 hover:underline"> <Link to="/auth" className="cursor-pointer ml-2 text-blue-600 hover:underline">
Sign in {t("auth.sign-in")}
</Link> </Link>
</p> </p>
</div> </div>

View File

@ -1,5 +1,5 @@
import { Button, Divider, Link, Textarea } from "@mui/joy"; import { Alert, Button, Divider, Link, Textarea } from "@mui/joy";
import { useEffect, useState } from "react"; import { useState } from "react";
import toast from "react-hot-toast"; import toast from "react-hot-toast";
import Icon from "@/components/Icon"; import Icon from "@/components/Icon";
import SubscriptionFAQ from "@/components/SubscriptionFAQ"; import SubscriptionFAQ from "@/components/SubscriptionFAQ";
@ -16,17 +16,12 @@ const SubscriptionSetting: React.FC = () => {
const isAdmin = currentUser.role === "ADMIN"; const isAdmin = currentUser.role === "ADMIN";
const profile = workspaceStore.profile; const profile = workspaceStore.profile;
useEffect(() => {
if (!isAdmin) {
window.location.href = "/";
}
}, []);
if (!isAdmin) {
return null;
}
const handleUpdateLicenseKey = async () => { const handleUpdateLicenseKey = async () => {
if (!isAdmin) {
toast.error("Only admin can upload license key");
return;
}
try { try {
const { subscription } = await subscriptionServiceClient.updateSubscription({ const { subscription } = await subscriptionServiceClient.updateSubscription({
licenseKey, licenseKey,
@ -60,7 +55,7 @@ const SubscriptionSetting: React.FC = () => {
<div className="w-full flex justify-between items-center mt-4"> <div className="w-full flex justify-between items-center mt-4">
<div> <div>
{profile.plan === PlanType.FREE && ( {profile.plan === PlanType.FREE && (
<Link href="https://yourselfhosted.lemonsqueezy.com/checkout?cart=df958121-81ad-4815-8b28-3f2d8e9c8e51" target="_blank"> <Link href="https://yourselfhosted.lemonsqueezy.com/checkout/buy/d03a2696-8a8b-49c9-9e19-d425e3884fd7" target="_blank">
Buy a license key Buy a license key
<Icon.ExternalLink className="w-4 h-auto ml-1" /> <Icon.ExternalLink className="w-4 h-auto ml-1" />
</Link> </Link>
@ -74,6 +69,10 @@ const SubscriptionSetting: React.FC = () => {
<Divider /> <Divider />
<section className="w-full pb-8 dark:bg-zinc-900 flex items-center justify-center"> <section className="w-full pb-8 dark:bg-zinc-900 flex items-center justify-center">
<div className="w-full px-6"> <div className="w-full px-6">
<Alert className="!inline-block mb-12">
Slash is open source bookmarks and link sharing platform. Our source code is available and accessible on{" "}
<Link href="https://github.com/boojack/slash">GitHub</Link> so anyone can get it, inspect it and review it.
</Alert>
<div className="w-full grid grid-cols-1 gap-12 mt-8 md:grid-cols-3"> <div className="w-full grid grid-cols-1 gap-12 mt-8 md:grid-cols-3">
<div className="flex flex-col p-6 bg-white dark:bg-zinc-800 shadow-lg rounded-lg justify-between border border-gray-300 dark:border-zinc-700"> <div className="flex flex-col p-6 bg-white dark:bg-zinc-800 shadow-lg rounded-lg justify-between border border-gray-300 dark:border-zinc-700">
<div> <div>
@ -138,7 +137,7 @@ const SubscriptionSetting: React.FC = () => {
<Link <Link
className="w-full" className="w-full"
underline="none" underline="none"
href="https://yourselfhosted.lemonsqueezy.com/checkout?cart=df958121-81ad-4815-8b28-3f2d8e9c8e51" href="https://yourselfhosted.lemonsqueezy.com/checkout/buy/d03a2696-8a8b-49c9-9e19-d425e3884fd7"
target="_blank" target="_blank"
> >
<Button className="w-full bg-gradient-to-r from-pink-500 to-purple-500 shadow hover:opacity-80">Get Started</Button> <Button className="w-full bg-gradient-to-r from-pink-500 to-purple-500 shadow hover:opacity-80">Get Started</Button>

View File

@ -1,4 +1,5 @@
import { createBrowserRouter } from "react-router-dom"; import { createBrowserRouter } from "react-router-dom";
import NotFound from "@/pages/NotFound";
import SignIn from "@/pages/SignIn"; import SignIn from "@/pages/SignIn";
import SignUp from "@/pages/SignUp"; import SignUp from "@/pages/SignUp";
import SubscriptionSetting from "@/pages/SubscriptionSetting"; import SubscriptionSetting from "@/pages/SubscriptionSetting";
@ -53,6 +54,10 @@ const router = createBrowserRouter([
}, },
], ],
}, },
{
path: "*",
element: <NotFound />,
},
], ],
}, },
]); ]);

View File

@ -1,41 +0,0 @@
import { create } from "zustand";
import { persist } from "zustand/middleware";
import * as api from "../../helpers/api";
interface FaviconState {
cache: {
[key: string]: string;
};
getOrFetchUrlFavicon: (url: string) => Promise<string>;
}
const useFaviconStore = create<FaviconState>()(
persist(
(set, get) => ({
cache: {},
getOrFetchUrlFavicon: async (url: string) => {
const cache = get().cache;
if (cache[url]) {
return cache[url];
}
try {
const { data: favicon } = await api.getUrlFavicon(url);
if (favicon) {
cache[url] = favicon;
set(cache);
return favicon;
}
} catch (error) {
// do nothing
}
return "";
},
}),
{
name: "favicon_cache",
}
)
);
export default useFaviconStore;

View File

@ -2,6 +2,12 @@ import react from "@vitejs/plugin-react-swc";
import { resolve } from "path"; import { resolve } from "path";
import { defineConfig } from "vite"; import { defineConfig } from "vite";
let devProxyServer = "http://localhost:8082/";
if (process.env.DEV_PROXY_SERVER && process.env.DEV_PROXY_SERVER.length > 0) {
console.log("Use devProxyServer from environment: ", process.env.DEV_PROXY_SERVER);
devProxyServer = process.env.DEV_PROXY_SERVER;
}
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
plugins: [react()], plugins: [react()],
@ -9,13 +15,17 @@ export default defineConfig({
host: "0.0.0.0", host: "0.0.0.0",
port: 3000, port: 3000,
proxy: { proxy: {
"/api/": { "^/api": {
target: "http://localhost:8082/", target: devProxyServer,
changeOrigin: true, xfwd: true,
}, },
"/s/": { "^/slash.api.v2": {
target: "http://localhost:8082/", target: devProxyServer,
changeOrigin: true, xfwd: true,
},
"^/s/": {
target: devProxyServer,
xfwd: true,
}, },
}, },
}, },

5
go.mod
View File

@ -29,13 +29,10 @@ require (
) )
require ( require (
github.com/PuerkitoBio/goquery v1.6.0 // indirect
github.com/andybalholm/cascadia v1.1.0 // indirect
github.com/cenkalti/backoff/v4 v4.1.1 // indirect github.com/cenkalti/backoff/v4 v4.1.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect github.com/davecgh/go-spew v1.1.1 // indirect
github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
github.com/dustin/go-humanize v1.0.1 // indirect github.com/dustin/go-humanize v1.0.1 // indirect
github.com/friendsofgo/errors v0.9.2 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect github.com/golang/protobuf v1.5.3 // indirect
github.com/hashicorp/hcl v1.0.0 // indirect github.com/hashicorp/hcl v1.0.0 // indirect
@ -57,7 +54,6 @@ require (
go.uber.org/atomic v1.9.0 // indirect go.uber.org/atomic v1.9.0 // indirect
go.uber.org/multierr v1.8.0 // indirect go.uber.org/multierr v1.8.0 // indirect
golang.org/x/tools v0.6.0 // indirect golang.org/x/tools v0.6.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect google.golang.org/genproto v0.0.0-20230706204954-ccb25ca9f130 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20230726155614-23370e0ffb3e // indirect
gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/ini.v1 v1.67.0 // indirect
@ -82,7 +78,6 @@ require (
github.com/mssola/useragent v1.0.0 github.com/mssola/useragent v1.0.0
github.com/patrickmn/go-cache v2.1.0+incompatible github.com/patrickmn/go-cache v2.1.0+incompatible
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
go.deanishe.net/favicon v0.1.0
go.uber.org/zap v1.21.0 go.uber.org/zap v1.21.0
golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df golang.org/x/exp v0.0.0-20230626212559-97b1e661b5df
golang.org/x/mod v0.11.0 golang.org/x/mod v0.11.0

76
go.sum
View File

@ -39,8 +39,6 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/PuerkitoBio/goquery v1.6.0 h1:j7taAbelrdcsOlGeMenZxc2AWXD5fieT1/znArdnx94=
github.com/PuerkitoBio/goquery v1.6.0/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo= github.com/Shopify/sarama v1.19.0/go.mod h1:FVkBWblsNy7DGZRfXLU0O9RCGt5g3g3yEuWXgklEdEo=
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI= github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g= github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
@ -50,18 +48,14 @@ github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuy
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.12.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/apache/thrift v0.13.0/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ=
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A= github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU= github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo= github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
github.com/aws/aws-sdk-go v1.34.28/go.mod h1:H7NKnBqNVzoTJpGfLrQkkD+ytBA93eiDYi/+8rV9s48=
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g= github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
@ -116,8 +110,6 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB
github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20=
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE= github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps= github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
github.com/friendsofgo/errors v0.9.2 h1:X6NYxef4efCBdwI7BgS820zFaN7Cphrmb+Pljdzjtgk=
github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY=
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
@ -135,8 +127,6 @@ github.com/go-kit/kit v0.10.0/go.mod h1:xUsJbQ/Fp4kEt7AFgCuvyX4a71u8h9jB8tj/ORgO
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94=
github.com/go-openapi/strfmt v0.19.8/go.mod h1:qBBipho+3EoIqn6YDI+4RnQEtj6jT/IdKm+PAlXxSUc=
github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q= github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8ceBS/t7Q=
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
@ -145,32 +135,7 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+
github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY= github.com/go-playground/validator/v10 v10.2.0 h1:KgJ0snyC2R9VXYN2rneOtQcw5aHQB1Vv0sFl1UcHBOY=
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/gobuffalo/attrs v0.0.0-20190224210810-a9411de4debd/go.mod h1:4duuawTqi2wkkpB4ePgWMaai6/Kc6WEz83bhFwpHzj0=
github.com/gobuffalo/depgen v0.0.0-20190329151759-d478694a28d3/go.mod h1:3STtPUQYuzV0gBVOY3vy6CfMm/ljR4pABfrTeHNLHUY=
github.com/gobuffalo/depgen v0.1.0/go.mod h1:+ifsuy7fhi15RWncXQQKjWS9JPkdah5sZvtHc2RXGlg=
github.com/gobuffalo/envy v1.6.15/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
github.com/gobuffalo/flect v0.1.0/go.mod h1:d2ehjJqGOH/Kjqcoz+F7jHTBbmDb38yXA598Hb50EGs=
github.com/gobuffalo/flect v0.1.1/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/flect v0.1.3/go.mod h1:8JCgGVbRjJhVgD6399mQr4fx5rRfGKVzFjbj6RE/9UI=
github.com/gobuffalo/genny v0.0.0-20190329151137-27723ad26ef9/go.mod h1:rWs4Z12d1Zbf19rlsn0nurr75KqhYp52EAGGxTbBhNk=
github.com/gobuffalo/genny v0.0.0-20190403191548-3ca520ef0d9e/go.mod h1:80lIj3kVJWwOrXWWMRzzdhW3DsrdjILVil/SFKBzF28=
github.com/gobuffalo/genny v0.1.0/go.mod h1:XidbUqzak3lHdS//TPu2OgiFB+51Ur5f7CSnXZ/JDvo=
github.com/gobuffalo/genny v0.1.1/go.mod h1:5TExbEyY48pfunL4QSXxlDOmdsD44RRq4mVZ0Ex28Xk=
github.com/gobuffalo/gitgen v0.0.0-20190315122116-cc086187d211/go.mod h1:vEHJk/E9DmhejeLeNt7UVvlSGv3ziL+djtTr3yyzcOw=
github.com/gobuffalo/gogen v0.0.0-20190315121717-8f38393713f5/go.mod h1:V9QVDIxsgKNZs6L2IYiGR8datgMhB577vzTDqypH360=
github.com/gobuffalo/gogen v0.1.0/go.mod h1:8NTelM5qd8RZ15VjQTFkAW6qOMx5wBbW4dSCS3BY8gg=
github.com/gobuffalo/gogen v0.1.1/go.mod h1:y8iBtmHmGc4qa3urIyo1shvOD8JftTtfcKi+71xfDNE=
github.com/gobuffalo/logger v0.0.0-20190315122211-86e12af44bc2/go.mod h1:QdxcLw541hSGtBnhUc4gaNIXRjiDppFGaDqzbrBd3v8=
github.com/gobuffalo/mapi v1.0.1/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/mapi v1.0.2/go.mod h1:4VAGh89y6rVOvm5A8fKFxYG+wIW6LO1FMTG9hnKStFc=
github.com/gobuffalo/packd v0.0.0-20190315124812-a385830c7fc0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packd v0.1.0/go.mod h1:M2Juc+hhDXf/PnmBANFCqx4DM3wRbgDvnVWeG2RIxq4=
github.com/gobuffalo/packr/v2 v2.0.9/go.mod h1:emmyGweYTm6Kdper+iywB6YK5YzuKchGtJQZ0Odn4pQ=
github.com/gobuffalo/packr/v2 v2.2.0/go.mod h1:CaAwI0GPIAv+5wKLtv8Afwl+Cm78K/I/VCm/3ptBN+0=
github.com/gobuffalo/syncx v0.0.0-20190224160051-33c29581e754/go.mod h1:HhnNqWY95UYwwW3uSASeV7vtgYkT2t16hJgV3AEPUpw=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee h1:s+21KNqlpePfkah2I+gwHF8xmJWRjooY+5248k6m4A0=
github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo= github.com/gobwas/httphead v0.0.0-20180130184737-2c6c146eadee/go.mod h1:L0fX3K22YWvt/FAX9NnzrNzcI4wNYi9Yku4O0LKYflo=
github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8= github.com/gobwas/pool v0.2.0 h1:QEmUOlnSjWtnpRGHF3SauEiOsy82Cup83Vf2LcMlnc8=
@ -217,7 +182,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
@ -250,7 +214,6 @@ github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbu
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
@ -304,11 +267,7 @@ github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANyt
github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc=
github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo=
github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag=
github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= github.com/jmespath/go-jmespath v0.0.0-20180206201540-c2b33e8439af/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
@ -323,18 +282,14 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/karrick/godirwalk v1.8.0/go.mod h1:H5KPZjojv4lE+QYImBI8xVtrBRgYrIVsaRPx4tDPEn4=
github.com/karrick/godirwalk v1.10.3/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/klauspost/compress v1.9.5/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.10.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg= github.com/klauspost/compress v1.11.7 h1:0hzRabrMN4tSTvMfnL3SCv1ZGeAP23ynzodBgaHeMeg=
github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/klauspost/compress v1.11.7/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
@ -356,8 +311,6 @@ github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0U
github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ=
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/markbates/oncer v0.0.0-20181203154359-bf2de49a0be2/go.mod h1:Ld9puTsIW75CHf65OeIOkyKbteujpZVXDpWK6YGZbxE=
github.com/markbates/safe v1.0.1/go.mod h1:nAqgmRi7cY2nqMc92/bSEeQA+R4OheNU2T1kNSCBdG0=
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4= github.com/mattn/go-colorable v0.1.11/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -370,7 +323,6 @@ github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA= github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y=
github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
@ -391,7 +343,6 @@ github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lN
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe/go.mod h1:wL8QJuTMNUDYhXwkmfOly8iTdp5TEcJFWZD2D7SIkUc=
github.com/mssola/useragent v1.0.0 h1:WRlDpXyxHDNfvZaPEut5Biveq86Ze4o4EMffyMxmH5o= github.com/mssola/useragent v1.0.0 h1:WRlDpXyxHDNfvZaPEut5Biveq86Ze4o4EMffyMxmH5o=
github.com/mssola/useragent v1.0.0/go.mod h1:hz9Cqz4RXusgg1EdI4Al0INR62kP7aPSRNHnpU+b85Y= github.com/mssola/useragent v1.0.0/go.mod h1:hz9Cqz4RXusgg1EdI4Al0INR62kP7aPSRNHnpU+b85Y=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
@ -425,7 +376,6 @@ github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FI
github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc=
github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ=
github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml v1.7.0/go.mod h1:vwGMzjaWMwyfHwgIBhI2YUM4fB6nL6lVAvS1LBMMhTE=
github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ= github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZR9tGQ=
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4= github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac= github.com/performancecopilot/speed v3.0.0+incompatible/go.mod h1:/CLtqpZ5gBg1M9iaPbIdPPGyKcA8hKdoy6hAWba7Yac=
@ -467,8 +417,6 @@ github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qq
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.2.2/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=
github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs=
@ -481,8 +429,6 @@ github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
@ -500,7 +446,6 @@ github.com/spf13/cobra v1.6.1/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUq
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU= github.com/spf13/viper v1.15.0 h1:js3yy885G8xwJa6iOISGFwd+qlUo5AvyXb7CiihdtiU=
@ -510,14 +455,12 @@ github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3
github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI= github.com/streadway/handy v0.0.0-20190108123426-d5acb3125c2a/go.mod h1:qNTQ5P5JnDBl6z3cMAg/SywNDC5ABu5ApDIw6lUbRmI=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
@ -526,7 +469,6 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8= github.com/subosito/gotenv v1.4.2 h1:X1TuBLAMDFbaTAChgCBLu3DU3UPyELpnF2jjJ2cz/S8=
github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0= github.com/subosito/gotenv v1.4.2/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo= github.com/ugorji/go v1.1.7 h1:/68gy2h+1mWMrwZFeD1kQialdSzAb432dtpeJ42ovdo=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
@ -539,19 +481,14 @@ github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyC
github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.1/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
github.com/xdg/stringprep v0.0.0-20180714160509-73f8eece6fdc/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.deanishe.net/favicon v0.1.0 h1:Afy941gjRik+DjUUcYHUxcztFEeFse2ITBkMMOlgefM=
go.deanishe.net/favicon v0.1.0/go.mod h1:vIKVI+lUh8k3UAzaN4gjC+cpyatLQWmx0hVX4vLE8jU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg= go.etcd.io/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
go.mongodb.org/mongo-driver v1.4.2/go.mod h1:WcMNYLx/IlOxLe6JRJiv2uXuCz6zBLndR4SoGjYphSc=
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk= go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@ -581,9 +518,7 @@ go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190422162423-af44ce270edf/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
@ -631,7 +566,6 @@ golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU=
golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@ -689,7 +623,6 @@ golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190412183630-56d357773e84/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
@ -709,13 +642,10 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190419153524-e8e3143a4f4a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190531175056-4c3a928424d2/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -789,13 +719,9 @@ golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190329151228-23e29df326fe/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190416151739-9c9e1878f421/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190420181800-aa740d480789/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
golang.org/x/tools v0.0.0-20190531172133-b3315ee88b7d/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
@ -844,8 +770,6 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk=
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk= google.golang.org/api v0.3.1/go.mod h1:6wY9I6uQWHQ8EM57III9mq/AjF+i8G65rmVagqKMtkk=
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=

View File

@ -39,14 +39,12 @@ message WorkspaceSetting {
string license_key = 1; string license_key = 1;
// Whether to enable other users to sign up. // Whether to enable other users to sign up.
bool enable_signup = 2; bool enable_signup = 2;
// The relative path of the resource directory.
string resource_relative_path = 3;
// The custom style. // The custom style.
string custom_style = 4; string custom_style = 3;
// The custom script. // The custom script.
string custom_script = 5; string custom_script = 4;
// The auto backup setting. // The auto backup setting.
AutoBackupWorkspaceSetting auto_backup = 6; AutoBackupWorkspaceSetting auto_backup = 5;
} }
message AutoBackupWorkspaceSetting { message AutoBackupWorkspaceSetting {

View File

@ -1006,7 +1006,6 @@
| ----- | ---- | ----- | ----------- | | ----- | ---- | ----- | ----------- |
| license_key | [string](#string) | | | | license_key | [string](#string) | | |
| enable_signup | [bool](#bool) | | Whether to enable other users to sign up. | | enable_signup | [bool](#bool) | | Whether to enable other users to sign up. |
| resource_relative_path | [string](#string) | | The relative path of the resource directory. |
| custom_style | [string](#string) | | The custom style. | | custom_style | [string](#string) | | The custom style. |
| custom_script | [string](#string) | | The custom script. | | custom_script | [string](#string) | | The custom script. |
| auto_backup | [AutoBackupWorkspaceSetting](#slash-api-v2-AutoBackupWorkspaceSetting) | | The auto backup setting. | | auto_backup | [AutoBackupWorkspaceSetting](#slash-api-v2-AutoBackupWorkspaceSetting) | | The auto backup setting. |

View File

@ -113,14 +113,12 @@ type WorkspaceSetting struct {
LicenseKey string `protobuf:"bytes,1,opt,name=license_key,json=licenseKey,proto3" json:"license_key,omitempty"` LicenseKey string `protobuf:"bytes,1,opt,name=license_key,json=licenseKey,proto3" json:"license_key,omitempty"`
// Whether to enable other users to sign up. // Whether to enable other users to sign up.
EnableSignup bool `protobuf:"varint,2,opt,name=enable_signup,json=enableSignup,proto3" json:"enable_signup,omitempty"` EnableSignup bool `protobuf:"varint,2,opt,name=enable_signup,json=enableSignup,proto3" json:"enable_signup,omitempty"`
// The relative path of the resource directory.
ResourceRelativePath string `protobuf:"bytes,3,opt,name=resource_relative_path,json=resourceRelativePath,proto3" json:"resource_relative_path,omitempty"`
// The custom style. // The custom style.
CustomStyle string `protobuf:"bytes,4,opt,name=custom_style,json=customStyle,proto3" json:"custom_style,omitempty"` CustomStyle string `protobuf:"bytes,3,opt,name=custom_style,json=customStyle,proto3" json:"custom_style,omitempty"`
// The custom script. // The custom script.
CustomScript string `protobuf:"bytes,5,opt,name=custom_script,json=customScript,proto3" json:"custom_script,omitempty"` CustomScript string `protobuf:"bytes,4,opt,name=custom_script,json=customScript,proto3" json:"custom_script,omitempty"`
// The auto backup setting. // The auto backup setting.
AutoBackup *AutoBackupWorkspaceSetting `protobuf:"bytes,6,opt,name=auto_backup,json=autoBackup,proto3" json:"auto_backup,omitempty"` AutoBackup *AutoBackupWorkspaceSetting `protobuf:"bytes,5,opt,name=auto_backup,json=autoBackup,proto3" json:"auto_backup,omitempty"`
} }
func (x *WorkspaceSetting) Reset() { func (x *WorkspaceSetting) Reset() {
@ -169,13 +167,6 @@ func (x *WorkspaceSetting) GetEnableSignup() bool {
return false return false
} }
func (x *WorkspaceSetting) GetResourceRelativePath() string {
if x != nil {
return x.ResourceRelativePath
}
return ""
}
func (x *WorkspaceSetting) GetCustomStyle() string { func (x *WorkspaceSetting) GetCustomStyle() string {
if x != nil { if x != nil {
return x.CustomStyle return x.CustomStyle
@ -564,101 +555,98 @@ var file_api_v2_workspace_service_proto_rawDesc = []byte{
0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d,
0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x05, 0x20, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x05, 0x20,
0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70,
0x74, 0x22, 0xa1, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x74, 0x22, 0xeb, 0x01, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x6c, 0x69, 0x63, 0x65, 0x6e, 0x73,
0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x69, 0x63, 0x65, 0x5f, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6c, 0x69, 0x63,
0x65, 0x6e, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x6e, 0x73, 0x65, 0x4b, 0x65, 0x79, 0x12, 0x23, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c,
0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c,
0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x12, 0x34, 0x0a, 0x16, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x12, 0x21, 0x0a, 0x0c,
0x72, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01,
0x65, 0x5f, 0x70, 0x61, 0x74, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x72, 0x65, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12,
0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74,
0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x79, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63,
0x6c, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x72, 0x69, 0x70, 0x74, 0x12, 0x49, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x62, 0x61, 0x63,
0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x23, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6b, 0x75, 0x70, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x63, 0x75, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63,
0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x49, 0x0a, 0x0b, 0x61, 0x75,
0x74, 0x6f, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x06, 0x20, 0x01, 0x28, 0x0b, 0x32,
0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x41,
0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x42,
0x61, 0x63, 0x6b, 0x75, 0x70, 0x22, 0x7a, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63,
0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74,
0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x69, 0x6e, 0x67, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x22,
0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x7a, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72,
0x0f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a,
0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07,
0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f, 0x6e, 0x5f,
0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x65, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09,
0x70, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x52, 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e,
0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01,
0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x65, 0x70, 0x22, 0x1c, 0x0a, 0x1a, 0x47,
0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69,
0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74,
0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65,
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x66,
0x07, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x69, 0x6c, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73,
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x66, 0x69,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x6c, 0x65, 0x22, 0x1c, 0x0a, 0x1a, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61,
0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22,
0x7a, 0x0a, 0x1d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x22, 0x57, 0x0a, 0x1b, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b,
0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e,
0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67,
0x0a, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x5a, 0x0a, 0x1e, 0x55, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x22, 0x7a, 0x0a, 0x1d, 0x55, 0x70, 0x64,
0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74,
0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65,
0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c,
0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74,
0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x32, 0xce, 0x03, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x1f, 0x0a, 0x0b, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d,
0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x61, 0x73, 0x6b, 0x18, 0x02, 0x20, 0x03, 0x28, 0x09, 0x52, 0x0a, 0x75, 0x70, 0x64, 0x61, 0x74,
0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x65, 0x4d, 0x61, 0x73, 0x6b, 0x22, 0x5a, 0x0a, 0x1e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57,
0x66, 0x69, 0x6c, 0x65, 0x12, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52,
0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x38, 0x0a, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69,
0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x6e, 0x67, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1e, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68,
0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x07, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x32, 0xce, 0x03, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53,
0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f,
0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x28,
0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65,
0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c,
0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68,
0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x70, 0x61, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x13, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70,
0x74, 0x69, 0x6e, 0x67, 0x12, 0x28, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x70,
0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x72, 0x6f, 0x66, 0x69, 0x6c, 0x65, 0x12, 0x8d, 0x01, 0x0a, 0x13, 0x47, 0x65, 0x74, 0x57, 0x6f,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x28,
0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65,
0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68,
0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x47, 0x65, 0x74, 0x57, 0x6f, 0x72, 0x6b, 0x73,
0x70, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x99, 0x01, 0x0a, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f,
0x16, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x6e, 0x73, 0x65, 0x22, 0x21, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1b, 0x12, 0x19, 0x2f, 0x61, 0x70,
0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x2b, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x73,
0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x99, 0x01, 0x0a, 0x16, 0x55, 0x70, 0x64, 0x61, 0x74,
0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x67, 0x12, 0x2b, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32,
0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x2c,
0x73, 0x65, 0x22, 0x24, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x2e, 0x55, 0x70,
0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x64, 0x61, 0x74, 0x65, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74,
0x2f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x42, 0xac, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x74, 0x69, 0x6e, 0x67, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x24, 0x82, 0xd3,
0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x15, 0x57, 0xe4, 0x93, 0x02, 0x1e, 0x3a, 0x01, 0x2a, 0x22, 0x19, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32,
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x2f, 0x73, 0x65, 0x74, 0x74, 0x69,
0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6e, 0x67, 0x42, 0xac, 0x01, 0x0a, 0x10, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68,
0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x6f, 0x6a, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x61, 0x70, 0x69, 0x2e, 0x76, 0x32, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61,
0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x63, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01,
0x32, 0x3b, 0x61, 0x70, 0x69, 0x76, 0x32, 0xa2, 0x02, 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x5a, 0x2f, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x6f,
0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0c, 0x53, 0x6a, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f,
0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x18, 0x53, 0x6c, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x32, 0x3b, 0x61, 0x70, 0x69, 0x76,
0x61, 0x73, 0x68, 0x5c, 0x41, 0x70, 0x69, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x32, 0xa2, 0x02, 0x03, 0x53, 0x41, 0x58, 0xaa, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x2e, 0x56, 0x32, 0xca, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41,
0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56, 0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 0x70, 0x69, 0x5c, 0x56, 0x32, 0xe2, 0x02, 0x18, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x41, 0x70,
0x69, 0x5c, 0x56, 0x32, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
0xea, 0x02, 0x0e, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x41, 0x70, 0x69, 0x3a, 0x3a, 0x56,
0x32, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (

View File

@ -310,7 +310,6 @@
| license_key | [string](#string) | | | | license_key | [string](#string) | | |
| secret_session | [string](#string) | | | | secret_session | [string](#string) | | |
| enable_signup | [bool](#bool) | | | | enable_signup | [bool](#bool) | | |
| resource_relative_path | [string](#string) | | |
| custom_style | [string](#string) | | | | custom_style | [string](#string) | | |
| custom_script | [string](#string) | | | | custom_script | [string](#string) | | |
| auto_backup | [AutoBackupWorkspaceSetting](#slash-store-AutoBackupWorkspaceSetting) | | | | auto_backup | [AutoBackupWorkspaceSetting](#slash-store-AutoBackupWorkspaceSetting) | | |
@ -333,10 +332,9 @@
| WORKSPACE_SETTING_LICENSE_KEY | 1 | The license key. | | WORKSPACE_SETTING_LICENSE_KEY | 1 | The license key. |
| WORKSPACE_SETTING_SECRET_SESSION | 2 | The secret session key used to encrypt session data. | | WORKSPACE_SETTING_SECRET_SESSION | 2 | The secret session key used to encrypt session data. |
| WORKSAPCE_SETTING_ENABLE_SIGNUP | 3 | Whether to enable other users to sign up. | | WORKSAPCE_SETTING_ENABLE_SIGNUP | 3 | Whether to enable other users to sign up. |
| WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH | 4 | The relative path of the resource directory. | | WORKSPACE_SETTING_CUSTOM_STYLE | 4 | The custom style. |
| WORKSPACE_SETTING_CUSTOM_STYLE | 5 | The custom style. | | WORKSPACE_SETTING_CUSTOM_SCRIPT | 5 | The custom script. |
| WORKSPACE_SETTING_CUSTOM_SCRIPT | 6 | The custom script. | | WORKSPACE_SETTING_AUTO_BACKUP | 6 | The auto backup setting. |
| WORKSPACE_SETTING_AUTO_BACKUP | 7 | The auto backup setting. |

View File

@ -30,14 +30,12 @@ const (
WorkspaceSettingKey_WORKSPACE_SETTING_SECRET_SESSION WorkspaceSettingKey = 2 WorkspaceSettingKey_WORKSPACE_SETTING_SECRET_SESSION WorkspaceSettingKey = 2
// Whether to enable other users to sign up. // Whether to enable other users to sign up.
WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP WorkspaceSettingKey = 3 WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP WorkspaceSettingKey = 3
// The relative path of the resource directory.
WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH WorkspaceSettingKey = 4
// The custom style. // The custom style.
WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_STYLE WorkspaceSettingKey = 5 WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_STYLE WorkspaceSettingKey = 4
// The custom script. // The custom script.
WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT WorkspaceSettingKey = 6 WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT WorkspaceSettingKey = 5
// The auto backup setting. // The auto backup setting.
WorkspaceSettingKey_WORKSPACE_SETTING_AUTO_BACKUP WorkspaceSettingKey = 7 WorkspaceSettingKey_WORKSPACE_SETTING_AUTO_BACKUP WorkspaceSettingKey = 6
) )
// Enum value maps for WorkspaceSettingKey. // Enum value maps for WorkspaceSettingKey.
@ -47,20 +45,18 @@ var (
1: "WORKSPACE_SETTING_LICENSE_KEY", 1: "WORKSPACE_SETTING_LICENSE_KEY",
2: "WORKSPACE_SETTING_SECRET_SESSION", 2: "WORKSPACE_SETTING_SECRET_SESSION",
3: "WORKSAPCE_SETTING_ENABLE_SIGNUP", 3: "WORKSAPCE_SETTING_ENABLE_SIGNUP",
4: "WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH", 4: "WORKSPACE_SETTING_CUSTOM_STYLE",
5: "WORKSPACE_SETTING_CUSTOM_STYLE", 5: "WORKSPACE_SETTING_CUSTOM_SCRIPT",
6: "WORKSPACE_SETTING_CUSTOM_SCRIPT", 6: "WORKSPACE_SETTING_AUTO_BACKUP",
7: "WORKSPACE_SETTING_AUTO_BACKUP",
} }
WorkspaceSettingKey_value = map[string]int32{ WorkspaceSettingKey_value = map[string]int32{
"WORKSPACE_SETTING_KEY_UNSPECIFIED": 0, "WORKSPACE_SETTING_KEY_UNSPECIFIED": 0,
"WORKSPACE_SETTING_LICENSE_KEY": 1, "WORKSPACE_SETTING_LICENSE_KEY": 1,
"WORKSPACE_SETTING_SECRET_SESSION": 2, "WORKSPACE_SETTING_SECRET_SESSION": 2,
"WORKSAPCE_SETTING_ENABLE_SIGNUP": 3, "WORKSAPCE_SETTING_ENABLE_SIGNUP": 3,
"WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH": 4, "WORKSPACE_SETTING_CUSTOM_STYLE": 4,
"WORKSPACE_SETTING_CUSTOM_STYLE": 5, "WORKSPACE_SETTING_CUSTOM_SCRIPT": 5,
"WORKSPACE_SETTING_CUSTOM_SCRIPT": 6, "WORKSPACE_SETTING_AUTO_BACKUP": 6,
"WORKSPACE_SETTING_AUTO_BACKUP": 7,
} }
) )
@ -102,7 +98,6 @@ type WorkspaceSetting struct {
// *WorkspaceSetting_LicenseKey // *WorkspaceSetting_LicenseKey
// *WorkspaceSetting_SecretSession // *WorkspaceSetting_SecretSession
// *WorkspaceSetting_EnableSignup // *WorkspaceSetting_EnableSignup
// *WorkspaceSetting_ResourceRelativePath
// *WorkspaceSetting_CustomStyle // *WorkspaceSetting_CustomStyle
// *WorkspaceSetting_CustomScript // *WorkspaceSetting_CustomScript
// *WorkspaceSetting_AutoBackup // *WorkspaceSetting_AutoBackup
@ -176,13 +171,6 @@ func (x *WorkspaceSetting) GetEnableSignup() bool {
return false return false
} }
func (x *WorkspaceSetting) GetResourceRelativePath() string {
if x, ok := x.GetValue().(*WorkspaceSetting_ResourceRelativePath); ok {
return x.ResourceRelativePath
}
return ""
}
func (x *WorkspaceSetting) GetCustomStyle() string { func (x *WorkspaceSetting) GetCustomStyle() string {
if x, ok := x.GetValue().(*WorkspaceSetting_CustomStyle); ok { if x, ok := x.GetValue().(*WorkspaceSetting_CustomStyle); ok {
return x.CustomStyle return x.CustomStyle
@ -220,20 +208,16 @@ type WorkspaceSetting_EnableSignup struct {
EnableSignup bool `protobuf:"varint,4,opt,name=enable_signup,json=enableSignup,proto3,oneof"` EnableSignup bool `protobuf:"varint,4,opt,name=enable_signup,json=enableSignup,proto3,oneof"`
} }
type WorkspaceSetting_ResourceRelativePath struct {
ResourceRelativePath string `protobuf:"bytes,5,opt,name=resource_relative_path,json=resourceRelativePath,proto3,oneof"`
}
type WorkspaceSetting_CustomStyle struct { type WorkspaceSetting_CustomStyle struct {
CustomStyle string `protobuf:"bytes,6,opt,name=custom_style,json=customStyle,proto3,oneof"` CustomStyle string `protobuf:"bytes,5,opt,name=custom_style,json=customStyle,proto3,oneof"`
} }
type WorkspaceSetting_CustomScript struct { type WorkspaceSetting_CustomScript struct {
CustomScript string `protobuf:"bytes,7,opt,name=custom_script,json=customScript,proto3,oneof"` CustomScript string `protobuf:"bytes,6,opt,name=custom_script,json=customScript,proto3,oneof"`
} }
type WorkspaceSetting_AutoBackup struct { type WorkspaceSetting_AutoBackup struct {
AutoBackup *AutoBackupWorkspaceSetting `protobuf:"bytes,8,opt,name=auto_backup,json=autoBackup,proto3,oneof"` AutoBackup *AutoBackupWorkspaceSetting `protobuf:"bytes,7,opt,name=auto_backup,json=autoBackup,proto3,oneof"`
} }
func (*WorkspaceSetting_LicenseKey) isWorkspaceSetting_Value() {} func (*WorkspaceSetting_LicenseKey) isWorkspaceSetting_Value() {}
@ -242,8 +226,6 @@ func (*WorkspaceSetting_SecretSession) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_EnableSignup) isWorkspaceSetting_Value() {} func (*WorkspaceSetting_EnableSignup) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_ResourceRelativePath) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_CustomStyle) isWorkspaceSetting_Value() {} func (*WorkspaceSetting_CustomStyle) isWorkspaceSetting_Value() {}
func (*WorkspaceSetting_CustomScript) isWorkspaceSetting_Value() {} func (*WorkspaceSetting_CustomScript) isWorkspaceSetting_Value() {}
@ -323,7 +305,7 @@ var File_store_workspace_setting_proto protoreflect.FileDescriptor
var file_store_workspace_setting_proto_rawDesc = []byte{ var file_store_workspace_setting_proto_rawDesc = []byte{
0x0a, 0x1d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x0a, 0x1d, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2f, 0x77, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63,
0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x65, 0x5f, 0x73, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12,
0x0b, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0x92, 0x03, 0x0a, 0x0b, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x22, 0xda, 0x02, 0x0a,
0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e,
0x67, 0x12, 0x32, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20, 0x67, 0x12, 0x32, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x20,
0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x57, 0x6f, 0x72, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x57, 0x6f, 0x72,
@ -335,59 +317,53 @@ var file_store_workspace_setting_proto_rawDesc = []byte{
0x48, 0x00, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x48, 0x00, 0x52, 0x0d, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f,
0x6e, 0x12, 0x25, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e, 0x6e, 0x12, 0x25, 0x0a, 0x0d, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x5f, 0x73, 0x69, 0x67, 0x6e,
0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62, 0x75, 0x70, 0x18, 0x04, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x0c, 0x65, 0x6e, 0x61, 0x62,
0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x12, 0x36, 0x0a, 0x16, 0x72, 0x65, 0x73, 0x6f, 0x6c, 0x65, 0x53, 0x69, 0x67, 0x6e, 0x75, 0x70, 0x12, 0x23, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74,
0x75, 0x72, 0x63, 0x65, 0x5f, 0x72, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x5f, 0x70, 0x61, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00,
0x74, 0x68, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x14, 0x72, 0x65, 0x73, 0x6f, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x25, 0x0a,
0x75, 0x72, 0x63, 0x65, 0x52, 0x65, 0x6c, 0x61, 0x74, 0x69, 0x76, 0x65, 0x50, 0x61, 0x74, 0x68, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x06,
0x12, 0x23, 0x0a, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x73, 0x74, 0x79, 0x6c, 0x65, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63,
0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0b, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x72, 0x69, 0x70, 0x74, 0x12, 0x4a, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x6f, 0x5f, 0x62, 0x61, 0x63,
0x53, 0x74, 0x79, 0x6c, 0x65, 0x12, 0x25, 0x0a, 0x0d, 0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x5f, 0x6b, 0x75, 0x70, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x73, 0x6c, 0x61, 0x73,
0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x0c, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b,
0x63, 0x75, 0x73, 0x74, 0x6f, 0x6d, 0x53, 0x63, 0x72, 0x69, 0x70, 0x74, 0x12, 0x4a, 0x0a, 0x0b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69,
0x61, 0x75, 0x74, 0x6f, 0x5f, 0x62, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x18, 0x08, 0x20, 0x01, 0x28, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70,
0x0b, 0x32, 0x27, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x2e, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x7a, 0x0a, 0x1a, 0x41, 0x75, 0x74,
0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65,
0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x48, 0x00, 0x52, 0x0a, 0x61, 0x75, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c,
0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x42, 0x07, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65,
0x65, 0x22, 0x7a, 0x0a, 0x1a, 0x41, 0x75, 0x74, 0x6f, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x57, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f, 0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73,
0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x12, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x6f, 0x6e,
0x18, 0x0a, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x08, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61,
0x52, 0x07, 0x65, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x64, 0x12, 0x27, 0x0a, 0x0f, 0x63, 0x72, 0x6f, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03, 0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61,
0x6e, 0x5f, 0x65, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x78, 0x4b, 0x65, 0x65, 0x70, 0x2a, 0x96, 0x02, 0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70,
0x28, 0x09, 0x52, 0x0e, 0x63, 0x72, 0x6f, 0x6e, 0x45, 0x78, 0x70, 0x72, 0x65, 0x73, 0x73, 0x69, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a,
0x6f, 0x6e, 0x12, 0x19, 0x0a, 0x08, 0x6d, 0x61, 0x78, 0x5f, 0x6b, 0x65, 0x65, 0x70, 0x18, 0x03, 0x21, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49,
0x20, 0x01, 0x28, 0x05, 0x52, 0x07, 0x6d, 0x61, 0x78, 0x4b, 0x65, 0x65, 0x70, 0x2a, 0xc4, 0x02, 0x4e, 0x47, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49,
0x0a, 0x13, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43,
0x6e, 0x67, 0x4b, 0x65, 0x79, 0x12, 0x25, 0x0a, 0x21, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53,
0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4b, 0x45, 0x59, 0x5f, 0x55, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x24, 0x0a, 0x20, 0x57, 0x4f, 0x52, 0x4b, 0x53,
0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x21, 0x0a, 0x1d, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x45, 0x43,
0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x52, 0x45, 0x54, 0x5f, 0x53, 0x45, 0x53, 0x53, 0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x23, 0x0a,
0x47, 0x5f, 0x4c, 0x49, 0x43, 0x45, 0x4e, 0x53, 0x45, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x41, 0x50, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49,
0x24, 0x0a, 0x20, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x4e, 0x47, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x45, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x55, 0x50,
0x54, 0x49, 0x4e, 0x47, 0x5f, 0x53, 0x45, 0x43, 0x52, 0x45, 0x54, 0x5f, 0x53, 0x45, 0x53, 0x53, 0x10, 0x03, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f,
0x49, 0x4f, 0x4e, 0x10, 0x02, 0x12, 0x23, 0x0a, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x41, 0x50, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x53,
0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x45, 0x4e, 0x41, 0x42, 0x4c, 0x54, 0x59, 0x4c, 0x45, 0x10, 0x04, 0x12, 0x23, 0x0a, 0x1f, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50,
0x45, 0x5f, 0x53, 0x49, 0x47, 0x4e, 0x55, 0x50, 0x10, 0x03, 0x12, 0x2c, 0x0a, 0x28, 0x57, 0x4f, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x55, 0x53, 0x54,
0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x4f, 0x4d, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x05, 0x12, 0x21, 0x0a, 0x1d, 0x57,
0x52, 0x45, 0x53, 0x4f, 0x55, 0x52, 0x43, 0x45, 0x5f, 0x52, 0x45, 0x4c, 0x41, 0x54, 0x49, 0x56, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47,
0x45, 0x5f, 0x50, 0x41, 0x54, 0x48, 0x10, 0x04, 0x12, 0x22, 0x0a, 0x1e, 0x57, 0x4f, 0x52, 0x4b, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x55, 0x50, 0x10, 0x06, 0x42, 0x9f,
0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x43, 0x55, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f,
0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x53, 0x54, 0x59, 0x4c, 0x45, 0x10, 0x05, 0x12, 0x23, 0x0a, 0x1f, 0x72, 0x65, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x61, 0x63, 0x65, 0x53, 0x65, 0x74,
0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x45, 0x54, 0x54, 0x49, 0x4e, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x28, 0x67, 0x69, 0x74,
0x47, 0x5f, 0x43, 0x55, 0x53, 0x54, 0x4f, 0x4d, 0x5f, 0x53, 0x43, 0x52, 0x49, 0x50, 0x54, 0x10, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x6f, 0x6a, 0x61, 0x63, 0x6b, 0x2f,
0x06, 0x12, 0x21, 0x0a, 0x1d, 0x57, 0x4f, 0x52, 0x4b, 0x53, 0x50, 0x41, 0x43, 0x45, 0x5f, 0x53, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f,
0x45, 0x54, 0x54, 0x49, 0x4e, 0x47, 0x5f, 0x41, 0x55, 0x54, 0x4f, 0x5f, 0x42, 0x41, 0x43, 0x4b, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x53, 0x58, 0xaa, 0x02, 0x0b, 0x53, 0x6c,
0x55, 0x50, 0x10, 0x07, 0x42, 0x9f, 0x01, 0x0a, 0x0f, 0x63, 0x6f, 0x6d, 0x2e, 0x73, 0x6c, 0x61, 0x61, 0x73, 0x68, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73,
0x73, 0x68, 0x2e, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x42, 0x15, 0x57, 0x6f, 0x72, 0x6b, 0x73, 0x70, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c,
0x61, 0x63, 0x65, 0x53, 0x65, 0x74, 0x74, 0x69, 0x6e, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74,
0x01, 0x5a, 0x28, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x6f, 0x61, 0xea, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a, 0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65,
0x6f, 0x6a, 0x61, 0x63, 0x6b, 0x2f, 0x73, 0x6c, 0x61, 0x73, 0x68, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
0x6f, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0xa2, 0x02, 0x03, 0x53, 0x53,
0x58, 0xaa, 0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xca,
0x02, 0x0b, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0xe2, 0x02, 0x17,
0x53, 0x6c, 0x61, 0x73, 0x68, 0x5c, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x5c, 0x47, 0x50, 0x42, 0x4d,
0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x0c, 0x53, 0x6c, 0x61, 0x73, 0x68, 0x3a,
0x3a, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
} }
var ( var (
@ -454,7 +430,6 @@ func file_store_workspace_setting_proto_init() {
(*WorkspaceSetting_LicenseKey)(nil), (*WorkspaceSetting_LicenseKey)(nil),
(*WorkspaceSetting_SecretSession)(nil), (*WorkspaceSetting_SecretSession)(nil),
(*WorkspaceSetting_EnableSignup)(nil), (*WorkspaceSetting_EnableSignup)(nil),
(*WorkspaceSetting_ResourceRelativePath)(nil),
(*WorkspaceSetting_CustomStyle)(nil), (*WorkspaceSetting_CustomStyle)(nil),
(*WorkspaceSetting_CustomScript)(nil), (*WorkspaceSetting_CustomScript)(nil),
(*WorkspaceSetting_AutoBackup)(nil), (*WorkspaceSetting_AutoBackup)(nil),

View File

@ -11,10 +11,9 @@ message WorkspaceSetting {
string license_key = 2; string license_key = 2;
string secret_session = 3; string secret_session = 3;
bool enable_signup = 4; bool enable_signup = 4;
string resource_relative_path = 5; string custom_style = 5;
string custom_style = 6; string custom_script = 6;
string custom_script = 7; AutoBackupWorkspaceSetting auto_backup = 7;
AutoBackupWorkspaceSetting auto_backup = 8;
} }
} }
@ -26,14 +25,12 @@ enum WorkspaceSettingKey {
WORKSPACE_SETTING_SECRET_SESSION = 2; WORKSPACE_SETTING_SECRET_SESSION = 2;
// Whether to enable other users to sign up. // Whether to enable other users to sign up.
WORKSAPCE_SETTING_ENABLE_SIGNUP = 3; WORKSAPCE_SETTING_ENABLE_SIGNUP = 3;
// The relative path of the resource directory.
WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH = 4;
// The custom style. // The custom style.
WORKSPACE_SETTING_CUSTOM_STYLE = 5; WORKSPACE_SETTING_CUSTOM_STYLE = 4;
// The custom script. // The custom script.
WORKSPACE_SETTING_CUSTOM_SCRIPT = 6; WORKSPACE_SETTING_CUSTOM_SCRIPT = 5;
// The auto backup setting. // The auto backup setting.
WORKSPACE_SETTING_AUTO_BACKUP = 7; WORKSPACE_SETTING_AUTO_BACKUP = 6;
} }
message AutoBackupWorkspaceSetting { message AutoBackupWorkspaceSetting {

View File

@ -20,6 +20,7 @@ import (
storepb "github.com/boojack/slash/proto/gen/store" storepb "github.com/boojack/slash/proto/gen/store"
"github.com/boojack/slash/server/profile" "github.com/boojack/slash/server/profile"
"github.com/boojack/slash/server/service/license" "github.com/boojack/slash/server/service/license"
"github.com/boojack/slash/server/service/resource"
"github.com/boojack/slash/store" "github.com/boojack/slash/store"
) )
@ -110,7 +111,7 @@ func NewServer(ctx context.Context, profile *profile.Profile, store *store.Store
} }
// Register resource service. // Register resource service.
resourceService := NewResourceService(profile, store) resourceService := resource.NewResourceService(profile, store)
resourceService.Register(rootGroup) resourceService.Register(rootGroup)
return s, nil return s, nil

View File

@ -1,4 +1,4 @@
package server package resource
import ( import (
"bytes" "bytes"
@ -9,11 +9,14 @@ import (
"github.com/h2non/filetype" "github.com/h2non/filetype"
"github.com/labstack/echo/v4" "github.com/labstack/echo/v4"
storepb "github.com/boojack/slash/proto/gen/store"
"github.com/boojack/slash/server/profile" "github.com/boojack/slash/server/profile"
"github.com/boojack/slash/store" "github.com/boojack/slash/store"
) )
const (
resourceRelativePath = "resources"
)
type ResourceService struct { type ResourceService struct {
Profile *profile.Profile Profile *profile.Profile
Store *store.Store Store *store.Store
@ -27,21 +30,9 @@ func NewResourceService(profile *profile.Profile, store *store.Store) *ResourceS
} }
// Register registers the resource service to the echo server. // Register registers the resource service to the echo server.
func (s *ResourceService) Register(g *echo.Group) { func (*ResourceService) Register(g *echo.Group) {
g.GET("/resources/:id", func(c echo.Context) error { g.GET("/resources/:id", func(c echo.Context) error {
ctx := c.Request().Context()
resourceID := c.Param("resourceId") resourceID := c.Param("resourceId")
resourceRelativePathSetting, err := s.Store.GetWorkspaceSetting(ctx, &store.FindWorkspaceSetting{
Key: storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH,
})
if err != nil {
return echo.NewHTTPError(http.StatusBadRequest, "failed to workspace resource relative path setting").SetInternal(err)
}
if resourceRelativePathSetting == nil {
return echo.NewHTTPError(http.StatusBadRequest, "found no workspace resource relative path setting")
}
resourceRelativePath := resourceRelativePathSetting.GetResourceRelativePath()
resourcePath := fmt.Sprintf("%s/%s", resourceRelativePath, resourceID) resourcePath := fmt.Sprintf("%s/%s", resourceRelativePath, resourceID)
buf, err := os.ReadFile(resourcePath) buf, err := os.ReadFile(resourcePath)
if err != nil { if err != nil {

View File

@ -9,10 +9,10 @@ import (
// Version is the service current released version. // Version is the service current released version.
// Semantic versioning: https://semver.org/ // Semantic versioning: https://semver.org/
var Version = "0.4.4" var Version = "0.4.5"
// DevVersion is the service current development version. // DevVersion is the service current development version.
var DevVersion = "0.4.4" var DevVersion = "0.4.5"
func GetCurrentVersion(mode string) string { func GetCurrentVersion(mode string) string {
if mode == "dev" || mode == "demo" { if mode == "dev" || mode == "demo" {

View File

@ -62,72 +62,80 @@ func (db *DB) Open(ctx context.Context) (err error) {
return errors.Wrapf(err, "failed to open db with dsn: %s", db.profile.DSN) return errors.Wrapf(err, "failed to open db with dsn: %s", db.profile.DSN)
} }
db.DBInstance = sqliteDB db.DBInstance = sqliteDB
currentVersion := version.GetCurrentVersion(db.profile.Mode)
if db.profile.Mode == "prod" { if db.profile.Mode == "prod" {
_, err := os.Stat(db.profile.DSN) _, err := os.Stat(db.profile.DSN)
if err != nil { if err != nil {
// If db file not exists, we should create a new one with latest schema. if !errors.Is(err, os.ErrNotExist) {
if errors.Is(err, os.ErrNotExist) {
if err := db.applyLatestSchema(ctx); err != nil {
return errors.Wrap(err, "failed to apply latest schema")
}
} else {
return errors.Wrap(err, "failed to get db file stat") return errors.Wrap(err, "failed to get db file stat")
} }
} else {
// If db file exists, we should check if we need to migrate the database. // If db file not exists, we should create a new one with latest schema.
currentVersion := version.GetCurrentVersion(db.profile.Mode) err := db.applyLatestSchema(ctx)
migrationHistoryList, err := db.FindMigrationHistoryList(ctx, &MigrationHistoryFind{})
if err != nil { if err != nil {
return errors.Wrap(err, "failed to find migration history") return errors.Wrap(err, "failed to apply latest schema")
} }
if len(migrationHistoryList) == 0 { _, err = db.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{
_, err := db.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{ Version: currentVersion,
Version: currentVersion, })
}) if err != nil {
if err != nil { return errors.Wrap(err, "failed to upsert migration history")
return errors.Wrap(err, "failed to upsert migration history")
}
return nil
} }
return nil
}
migrationHistoryVersionList := []string{} // If db file exists, we should check if we need to migrate the database.
for _, migrationHistory := range migrationHistoryList { migrationHistoryList, err := db.FindMigrationHistoryList(ctx, &MigrationHistoryFind{})
migrationHistoryVersionList = append(migrationHistoryVersionList, migrationHistory.Version) if err != nil {
return errors.Wrap(err, "failed to find migration history")
}
if len(migrationHistoryList) == 0 {
_, err := db.UpsertMigrationHistory(ctx, &MigrationHistoryUpsert{
Version: currentVersion,
})
if err != nil {
return errors.Wrap(err, "failed to upsert migration history")
} }
sort.Sort(version.SortVersion(migrationHistoryVersionList)) return nil
latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1] }
if version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) { migrationHistoryVersionList := []string{}
minorVersionList := getMinorVersionList() for _, migrationHistory := range migrationHistoryList {
migrationHistoryVersionList = append(migrationHistoryVersionList, migrationHistory.Version)
}
sort.Sort(version.SortVersion(migrationHistoryVersionList))
latestMigrationHistoryVersion := migrationHistoryVersionList[len(migrationHistoryVersionList)-1]
// backup the raw database file before migration if version.IsVersionGreaterThan(version.GetSchemaVersion(currentVersion), latestMigrationHistoryVersion) {
rawBytes, err := os.ReadFile(db.profile.DSN) minorVersionList := getMinorVersionList()
if err != nil {
return errors.Wrap(err, "failed to read raw database file")
}
backupDBFilePath := fmt.Sprintf("%s/slash_%s_%d_backup.db", db.profile.Data, db.profile.Version, time.Now().Unix())
if err := os.WriteFile(backupDBFilePath, rawBytes, 0644); err != nil {
return errors.Wrap(err, "failed to write raw database file")
}
println("succeed to copy a backup database file")
println("start migrate") // backup the raw database file before migration
for _, minorVersion := range minorVersionList { rawBytes, err := os.ReadFile(db.profile.DSN)
normalizedVersion := minorVersion + ".0" if err != nil {
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) { return errors.Wrap(err, "failed to read raw database file")
println("applying migration for", normalizedVersion) }
if err := db.applyMigrationForMinorVersion(ctx, minorVersion); err != nil { backupDBFilePath := fmt.Sprintf("%s/slash_%s_%d_backup.db", db.profile.Data, db.profile.Version, time.Now().Unix())
return errors.Wrap(err, "failed to apply minor version migration") if err := os.WriteFile(backupDBFilePath, rawBytes, 0644); err != nil {
} return errors.Wrap(err, "failed to write raw database file")
}
println("succeed to copy a backup database file")
println("start migrate")
for _, minorVersion := range minorVersionList {
normalizedVersion := minorVersion + ".0"
if version.IsVersionGreaterThan(normalizedVersion, latestMigrationHistoryVersion) && version.IsVersionGreaterOrEqualThan(currentVersion, normalizedVersion) {
println("applying migration for", normalizedVersion)
if err := db.applyMigrationForMinorVersion(ctx, minorVersion); err != nil {
return errors.Wrap(err, "failed to apply minor version migration")
} }
} }
println("end migrate") }
println("end migrate")
// remove the created backup db file after migrate succeed // remove the created backup db file after migrate succeed
if err := os.Remove(backupDBFilePath); err != nil { if err := os.Remove(backupDBFilePath); err != nil {
println(fmt.Sprintf("Failed to remove temp database file, err %v", err)) println(fmt.Sprintf("Failed to remove temp database file, err %v", err))
}
} }
} }
} else { } else {
@ -157,7 +165,7 @@ func (db *DB) applyLatestSchema(ctx context.Context) error {
if db.profile.Mode == "prod" { if db.profile.Mode == "prod" {
schemaMode = "prod" schemaMode = "prod"
} }
latestSchemaPath := fmt.Sprintf("%s/%s/%s", "migration", schemaMode, latestSchemaFileName) latestSchemaPath := fmt.Sprintf("migration/%s/%s", schemaMode, latestSchemaFileName)
buf, err := migrationFS.ReadFile(latestSchemaPath) buf, err := migrationFS.ReadFile(latestSchemaPath)
if err != nil { if err != nil {
return errors.Wrapf(err, "failed to read latest schema %q", latestSchemaPath) return errors.Wrapf(err, "failed to read latest schema %q", latestSchemaPath)
@ -170,9 +178,9 @@ func (db *DB) applyLatestSchema(ctx context.Context) error {
} }
func (db *DB) applyMigrationForMinorVersion(ctx context.Context, minorVersion string) error { func (db *DB) applyMigrationForMinorVersion(ctx context.Context, minorVersion string) error {
filenames, err := fs.Glob(migrationFS, fmt.Sprintf("%s/%s/*.sql", "migration/prod", minorVersion)) filenames, err := fs.Glob(migrationFS, fmt.Sprintf("migration/prod/%s/*.sql", minorVersion))
if err != nil { if err != nil {
return errors.Wrap(err, "failed to read ddl files") return errors.Wrap(err, "failed to read migrate files")
} }
sort.Strings(filenames) sort.Strings(filenames)
@ -203,13 +211,12 @@ func (db *DB) applyMigrationForMinorVersion(ctx context.Context, minorVersion st
} }
func (db *DB) seed(ctx context.Context) error { func (db *DB) seed(ctx context.Context) error {
filenames, err := fs.Glob(seedFS, fmt.Sprintf("%s/*.sql", "seed")) filenames, err := fs.Glob(seedFS, "seed/*.sql")
if err != nil { if err != nil {
return errors.Wrap(err, "failed to read seed files") return errors.Wrap(err, "failed to read seed files")
} }
sort.Strings(filenames) sort.Strings(filenames)
// Loop over all seed files and execute them in order. // Loop over all seed files and execute them in order.
for _, filename := range filenames { for _, filename := range filenames {
buf, err := seedFS.ReadFile(filename) buf, err := seedFS.ReadFile(filename)

View File

@ -32,8 +32,6 @@ func (s *Store) UpsertWorkspaceSetting(ctx context.Context, upsert *storepb.Work
valueString = upsert.GetSecretSession() valueString = upsert.GetSecretSession()
} else if upsert.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP { } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSAPCE_SETTING_ENABLE_SIGNUP {
valueString = strconv.FormatBool(upsert.GetEnableSignup()) valueString = strconv.FormatBool(upsert.GetEnableSignup())
} else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH {
valueString = upsert.GetResourceRelativePath()
} else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_STYLE { } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_STYLE {
valueString = upsert.GetCustomStyle() valueString = upsert.GetCustomStyle()
} else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT { } else if upsert.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT {
@ -98,8 +96,6 @@ func (s *Store) ListWorkspaceSettings(ctx context.Context, find *FindWorkspaceSe
return nil, err return nil, err
} }
workspaceSetting.Value = &storepb.WorkspaceSetting_EnableSignup{EnableSignup: enableSignup} workspaceSetting.Value = &storepb.WorkspaceSetting_EnableSignup{EnableSignup: enableSignup}
} else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_RESOURCE_RELATIVE_PATH {
workspaceSetting.Value = &storepb.WorkspaceSetting_ResourceRelativePath{ResourceRelativePath: valueString}
} else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_STYLE { } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_STYLE {
workspaceSetting.Value = &storepb.WorkspaceSetting_CustomStyle{CustomStyle: valueString} workspaceSetting.Value = &storepb.WorkspaceSetting_CustomStyle{CustomStyle: valueString}
} else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT { } else if workspaceSetting.Key == storepb.WorkspaceSettingKey_WORKSPACE_SETTING_CUSTOM_SCRIPT {