mirror of
https://github.com/aykhans/slash-e.git
synced 2025-04-18 21:19:44 +00:00
feat: support multiple type shortcuts link
This commit is contained in:
parent
fbf958cf3e
commit
fc27490766
@ -3,6 +3,7 @@ package v1
|
|||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
|
||||||
"github.com/boojack/shortify/store"
|
"github.com/boojack/shortify/store"
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
@ -39,7 +40,10 @@ func (s *APIV1Service) registerRedirectorRoutes(g *echo.Group) {
|
|||||||
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err)
|
return echo.NewHTTPError(http.StatusInternalServerError, "Failed to create activity").SetInternal(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.Redirect(http.StatusSeeOther, shortcut.Link)
|
if isValidURLString(shortcut.Link) {
|
||||||
|
return c.Redirect(http.StatusSeeOther, shortcut.Link)
|
||||||
|
}
|
||||||
|
return c.String(http.StatusOK, shortcut.Link)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -66,3 +70,8 @@ func (s *APIV1Service) createShortcutViewActivity(c echo.Context, shortcut *stor
|
|||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isValidURLString(s string) bool {
|
||||||
|
_, err := url.ParseRequestURI(s)
|
||||||
|
return err == nil
|
||||||
|
}
|
||||||
|
33
api/v1/redirector_test.go
Normal file
33
api/v1/redirector_test.go
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
package v1
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestIsValidURLString(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
link string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
link: "https://google.com",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: "http://google.com",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: "google.com",
|
||||||
|
expected: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
link: "mailto:email@example.com",
|
||||||
|
expected: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, test := range tests {
|
||||||
|
if isValidURLString(test.link) != test.expected {
|
||||||
|
t.Errorf("isValidURLString(%s) = %v, expected %v", test.link, !test.expected, test.expected)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -8,7 +8,6 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/boojack/shortify/internal/util"
|
|
||||||
"github.com/boojack/shortify/store"
|
"github.com/boojack/shortify/store"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
|
||||||
@ -86,9 +85,6 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) {
|
|||||||
if err := json.NewDecoder(c.Request().Body).Decode(create); err != nil {
|
if err := json.NewDecoder(c.Request().Body).Decode(create); err != nil {
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post shortcut request").SetInternal(err)
|
return echo.NewHTTPError(http.StatusBadRequest, "Malformatted post shortcut request").SetInternal(err)
|
||||||
}
|
}
|
||||||
if !validateLink(create.Link) {
|
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid link: %s", create.Link))
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcut, err := s.Store.CreateShortcut(ctx, &store.Shortcut{
|
shortcut, err := s.Store.CreateShortcut(ctx, &store.Shortcut{
|
||||||
CreatorID: userID,
|
CreatorID: userID,
|
||||||
@ -151,9 +147,6 @@ func (s *APIV1Service) registerShortcutRoutes(g *echo.Group) {
|
|||||||
name := strings.ToLower(*patch.Name)
|
name := strings.ToLower(*patch.Name)
|
||||||
patch.Name = &name
|
patch.Name = &name
|
||||||
}
|
}
|
||||||
if patch.Link != nil && !validateLink(*patch.Link) {
|
|
||||||
return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid link: %s", *patch.Link))
|
|
||||||
}
|
|
||||||
|
|
||||||
shortcutUpdate := &store.UpdateShortcut{
|
shortcutUpdate := &store.UpdateShortcut{
|
||||||
ID: shortcutID,
|
ID: shortcutID,
|
||||||
@ -369,7 +362,3 @@ func convertShortcutFromStore(shortcut *store.Shortcut) *Shortcut {
|
|||||||
Tags: tags,
|
Tags: tags,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func validateLink(link string) bool {
|
|
||||||
return util.HasPrefixes(link, "http://", "https://")
|
|
||||||
}
|
|
||||||
|
@ -24,6 +24,7 @@ const ShortcutView = (props: Props) => {
|
|||||||
const faviconStore = useFaviconStore();
|
const faviconStore = useFaviconStore();
|
||||||
const [favicon, setFavicon] = useState<string | undefined>(undefined);
|
const [favicon, setFavicon] = useState<string | undefined>(undefined);
|
||||||
const havePermission = user.role === "ADMIN" || shortcut.creatorId === user.id;
|
const havePermission = user.role === "ADMIN" || shortcut.creatorId === user.id;
|
||||||
|
const shortifyLink = absolutifyLink(`/s/${shortcut.name}`);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => {
|
faviconStore.getOrFetchUrlFavicon(shortcut.link).then((url) => {
|
||||||
@ -33,8 +34,8 @@ const ShortcutView = (props: Props) => {
|
|||||||
});
|
});
|
||||||
}, [shortcut.link]);
|
}, [shortcut.link]);
|
||||||
|
|
||||||
const handleCopyButtonClick = (shortcut: Shortcut) => {
|
const handleCopyButtonClick = () => {
|
||||||
copy(absolutifyLink(`/s/${shortcut.name}`));
|
copy(shortifyLink);
|
||||||
toast.success("Shortcut link copied to clipboard.");
|
toast.success("Shortcut link copied to clipboard.");
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -60,11 +61,11 @@ const ShortcutView = (props: Props) => {
|
|||||||
<Icon.Globe2 className="w-5 h-auto text-gray-500" />
|
<Icon.Globe2 className="w-5 h-auto text-gray-500" />
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<button className="items-center cursor-pointer hover:opacity-80" onClick={() => handleCopyButtonClick(shortcut)}>
|
<button className="items-center cursor-pointer hover:opacity-80" onClick={() => handleCopyButtonClick()}>
|
||||||
<span className="text-gray-400">s/</span>
|
<span className="text-gray-400">s/</span>
|
||||||
{shortcut.name}
|
{shortcut.name}
|
||||||
</button>
|
</button>
|
||||||
<a className="hidden group-hover:block ml-1 cursor-pointer hover:opacity-80" target="_blank" href={shortcut.link}>
|
<a className="hidden group-hover:block ml-1 cursor-pointer hover:opacity-80" target="_blank" href={shortifyLink}>
|
||||||
<Icon.ExternalLink className="w-4 h-auto text-gray-500" />
|
<Icon.ExternalLink className="w-4 h-auto text-gray-500" />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user