Compare commits
2 commits
3404b377d3
...
3926713d6c
Author | SHA1 | Date | |
---|---|---|---|
|
3926713d6c | ||
|
ed1ad7fafc |
|
@ -12,13 +12,16 @@
|
|||
"elm/html": "1.0.0",
|
||||
"elm/http": "2.0.0",
|
||||
"elm/json": "1.1.3",
|
||||
"elm/url": "1.0.0"
|
||||
"elm/url": "1.0.0",
|
||||
"rtfeldman/elm-css": "18.0.0"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/bytes": "1.0.8",
|
||||
"elm/file": "1.0.5",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.3"
|
||||
"elm/virtual-dom": "1.0.3",
|
||||
"robinheghan/murmur3": "1.0.0",
|
||||
"rtfeldman/elm-hex": "1.0.0"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
module CreatePage exposing (Model, Msg, init, update, view)
|
||||
|
||||
import Browser.Navigation as Nav
|
||||
import Html exposing (Html, text)
|
||||
import Html.Attributes as Attr
|
||||
import Html.Events as Events
|
||||
import Css
|
||||
import Css.Media as Media
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes as Attr
|
||||
import Html.Styled.Events as Events
|
||||
import Http
|
||||
import Input
|
||||
import Json.Decode as Decode exposing (Decoder)
|
||||
import Json.Decode.Pipeline exposing (required)
|
||||
import Json.Encode as Encode
|
||||
|
@ -23,12 +26,36 @@ init =
|
|||
}
|
||||
|
||||
|
||||
descriptionStyles : Css.Style
|
||||
descriptionStyles =
|
||||
Css.marginTop (Css.px 0)
|
||||
|
||||
|
||||
inputLineStyles : Css.Style
|
||||
inputLineStyles =
|
||||
Css.batch
|
||||
[ Css.displayFlex
|
||||
, Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.display Css.block ]
|
||||
]
|
||||
|
||||
|
||||
view : Model -> Html Msg
|
||||
view model =
|
||||
Html.div []
|
||||
[ Html.form [ Events.onSubmit (ClickedCreate model.link) ]
|
||||
[ Html.input [ Attr.type_ "text", Events.onInput LinkUpdated ] []
|
||||
, Html.button [ Attr.type_ "submit" ] [ text "Create doglink" ]
|
||||
div []
|
||||
[ form [ Events.onSubmit (ClickedCreate model.link) ]
|
||||
[ p [ Attr.css [ descriptionStyles ] ]
|
||||
[ text "A Doglink is just an alias, or a redirect for an existing link. Paste a link you want to redirect to into the field below!"
|
||||
]
|
||||
, div [ Attr.css [ inputLineStyles ] ]
|
||||
[ Input.text
|
||||
[ Attr.placeholder "https://example.com"
|
||||
, Events.onInput LinkUpdated
|
||||
]
|
||||
[]
|
||||
, Input.submitButton [] [ text "Create doglink" ]
|
||||
]
|
||||
]
|
||||
, case model.error of
|
||||
Just error ->
|
||||
|
@ -41,9 +68,9 @@ view model =
|
|||
|
||||
viewError : String -> Html msg
|
||||
viewError error =
|
||||
Html.div []
|
||||
[ Html.h3 [] [ text "Couldn't create doglink" ]
|
||||
, Html.p [] [ text error ]
|
||||
div []
|
||||
[ h3 [] [ text "Couldn't create doglink" ]
|
||||
, p [] [ text error ]
|
||||
]
|
||||
|
||||
|
||||
|
@ -56,7 +83,7 @@ type Msg
|
|||
update : String -> Nav.Key -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update origin key msg model =
|
||||
case msg of
|
||||
ClickedCreate link ->
|
||||
ClickedCreate _ ->
|
||||
( model, createLink origin model )
|
||||
|
||||
LinkUpdated link ->
|
||||
|
|
100
client/src/Input.elm
Normal file
100
client/src/Input.elm
Normal file
|
@ -0,0 +1,100 @@
|
|||
module Input exposing (copyButton, submitButton, text)
|
||||
|
||||
import Css
|
||||
import Css.Media as Media
|
||||
import Html.Styled as Html exposing (..)
|
||||
import Html.Styled.Attributes as Attr
|
||||
|
||||
|
||||
sharedStyles : Css.Style
|
||||
sharedStyles =
|
||||
Css.batch
|
||||
[ Css.borderStyle Css.solid
|
||||
, Css.borderWidth (Css.px 1)
|
||||
, Css.fontSize (Css.px 16)
|
||||
, Css.height (Css.px 40)
|
||||
, Css.lineHeight (Css.px 30)
|
||||
, Css.outline Css.none
|
||||
, Css.padding2 (Css.px 4) (Css.px 16)
|
||||
, Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.borderRadius (Css.px 4)
|
||||
, Css.width (Css.pct 100)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
inputStyles : Css.Style
|
||||
inputStyles =
|
||||
Css.batch
|
||||
[ Css.borderRadius4 (Css.px 4) (Css.px 0) (Css.px 0) (Css.px 4)
|
||||
, Css.borderColor (Css.hex "bbb")
|
||||
, Css.boxShadow5 Css.inset (Css.px 0) (Css.px 0) (Css.px 4) (Css.rgba 0 0 0 0.1)
|
||||
, Css.focus
|
||||
[ Css.borderColor (Css.hex "c92a60")
|
||||
, Css.boxShadow5 Css.inset (Css.px 0) (Css.px 0) (Css.px 3) (Css.hex "c92a60")
|
||||
]
|
||||
, Css.hover [ Css.borderColor (Css.hex "c92a60") ]
|
||||
, Css.flex (Css.int 1)
|
||||
, Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.flex Css.none ]
|
||||
, sharedStyles
|
||||
]
|
||||
|
||||
|
||||
buttonStyles : Css.Style
|
||||
buttonStyles =
|
||||
Css.batch
|
||||
[ Css.borderRadius4 (Css.px 0) (Css.px 4) (Css.px 4) (Css.px 0)
|
||||
, Css.cursor Css.pointer
|
||||
, Css.overflow Css.hidden
|
||||
, Css.whiteSpace Css.noWrap
|
||||
, Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.marginTop (Css.px 8) ]
|
||||
, sharedStyles
|
||||
]
|
||||
|
||||
|
||||
submitButtonStyles : Css.Style
|
||||
submitButtonStyles =
|
||||
Css.batch
|
||||
[ buttonStyles
|
||||
, Css.backgroundColor (Css.hex "c92a60")
|
||||
, Css.borderColor (Css.hex "9d2a60")
|
||||
, Css.color (Css.hex "fff")
|
||||
, Css.hover
|
||||
[ Css.backgroundColor (Css.hex "9d2a60")
|
||||
, Css.borderColor (Css.hex "662041")
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
copyButtonStyles : Css.Style
|
||||
copyButtonStyles =
|
||||
Css.batch
|
||||
[ buttonStyles
|
||||
, Css.backgroundColor (Css.hex "e5e5e5")
|
||||
, Css.borderColor (Css.hex "ccc")
|
||||
, Css.color (Css.hex "222")
|
||||
, Css.hover
|
||||
[ Css.backgroundColor (Css.hex "ddd")
|
||||
, Css.borderColor (Css.hex "aaa")
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
text : List (Attribute msg) -> List (Html msg) -> Html msg
|
||||
text attrs content =
|
||||
Html.input (Attr.css [ inputStyles ] :: Attr.type_ "text" :: attrs) content
|
||||
|
||||
|
||||
submitButton : List (Attribute msg) -> List (Html msg) -> Html msg
|
||||
submitButton attrs content =
|
||||
Html.button (Attr.css [ submitButtonStyles ] :: Attr.type_ "submit " :: attrs) content
|
||||
|
||||
|
||||
copyButton : List (Attribute msg) -> List (Html msg) -> Html msg
|
||||
copyButton attrs content =
|
||||
Html.button (Attr.css [ copyButtonStyles ] :: attrs) content
|
|
@ -3,7 +3,11 @@ module Main exposing (..)
|
|||
import Browser exposing (Document)
|
||||
import Browser.Navigation as Nav
|
||||
import CreatePage
|
||||
import Html exposing (Html, text)
|
||||
import Css
|
||||
import Css.Media as Media
|
||||
import Html
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes exposing (css, href, src)
|
||||
import ShowPage
|
||||
import Url exposing (Url)
|
||||
import Url.Parser as Parser exposing (Parser)
|
||||
|
@ -26,21 +30,73 @@ type Page
|
|||
| ShowPage ShowPage.Model
|
||||
|
||||
|
||||
sharedStyles : Css.Style
|
||||
sharedStyles =
|
||||
Css.batch
|
||||
[ Css.margin Css.auto
|
||||
, Css.maxWidth (Css.px 500)
|
||||
, Css.width (Css.pct 100)
|
||||
]
|
||||
|
||||
|
||||
headerSectionStyles : Css.Style
|
||||
headerSectionStyles =
|
||||
Css.batch
|
||||
[ Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.padding2 (Css.px 0) (Css.px 16) ]
|
||||
, sharedStyles
|
||||
]
|
||||
|
||||
|
||||
sectionStyles : Css.Style
|
||||
sectionStyles =
|
||||
Css.batch
|
||||
[ Css.backgroundColor (Css.hex "fff")
|
||||
, Css.borderRadius (Css.px 4)
|
||||
, Css.color (Css.hex "222")
|
||||
, Css.padding (Css.px 16)
|
||||
, Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.borderRadius (Css.px 0) ]
|
||||
, sharedStyles
|
||||
]
|
||||
|
||||
|
||||
headerStyles : Css.Style
|
||||
headerStyles =
|
||||
Css.marginBottom (Css.px 0)
|
||||
|
||||
|
||||
subHeaderStyles : Css.Style
|
||||
subHeaderStyles =
|
||||
Css.batch
|
||||
[ Css.fontStyle Css.italic
|
||||
, Css.marginTop (Css.px 0)
|
||||
]
|
||||
|
||||
|
||||
view : Model -> Document Msg
|
||||
view model =
|
||||
{ title = "Doglinks"
|
||||
, body =
|
||||
[ Html.h1 [] [ text "Doglinks" ]
|
||||
, Html.p [] [ text "Create your doglinks here" ]
|
||||
, case model.page of
|
||||
CreatePage page ->
|
||||
CreatePage.view page
|
||||
|> Html.map CreateMsg
|
||||
List.map toUnstyled
|
||||
[ section [ css [ headerSectionStyles ] ]
|
||||
[ h1 [ css [ headerStyles ] ] [ text "Doglinks" ]
|
||||
, p [ css [ subHeaderStyles ] ] [ text "Create your doglinks here" ]
|
||||
]
|
||||
, section
|
||||
[ css [ sectionStyles ] ]
|
||||
[ case model.page of
|
||||
CreatePage page ->
|
||||
CreatePage.view page
|
||||
|> Html.Styled.map CreateMsg
|
||||
|
||||
ShowPage page ->
|
||||
ShowPage.view model.origin page
|
||||
|> Html.map ShowMsg
|
||||
]
|
||||
ShowPage page ->
|
||||
ShowPage.view model.origin page
|
||||
|> Html.Styled.map ShowMsg
|
||||
]
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
@ -89,7 +145,7 @@ update msg model =
|
|||
ShowPage page ->
|
||||
let
|
||||
( pageModel, pageMsg ) =
|
||||
ShowPage.update model.key showMsg page
|
||||
ShowPage.update showMsg page
|
||||
in
|
||||
( { model | page = ShowPage pageModel }, Cmd.map ShowMsg pageMsg )
|
||||
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
port module ShowPage exposing (Model, Msg, init, subscriptions, update, view)
|
||||
|
||||
import Browser
|
||||
import Browser.Navigation as Nav
|
||||
import Html exposing (Html, text)
|
||||
import Html.Attributes as Attr
|
||||
import Html.Events as Events
|
||||
import Css
|
||||
import Css.Media as Media
|
||||
import Html.Styled exposing (..)
|
||||
import Html.Styled.Attributes as Attr
|
||||
import Html.Styled.Events as Events
|
||||
import Input
|
||||
import Process
|
||||
import Task
|
||||
|
||||
|
@ -22,17 +23,60 @@ init uuid =
|
|||
}
|
||||
|
||||
|
||||
headerStyles : Css.Style
|
||||
headerStyles =
|
||||
Css.batch
|
||||
[ Css.margin (Css.px 0)
|
||||
]
|
||||
|
||||
|
||||
descriptionStyles : Css.Style
|
||||
descriptionStyles =
|
||||
Css.batch []
|
||||
|
||||
|
||||
copyLineStyles : Css.Style
|
||||
copyLineStyles =
|
||||
Css.batch
|
||||
[ Css.displayFlex
|
||||
, Media.withMedia
|
||||
[ Media.only Media.screen [ Media.maxWidth (Css.px 500) ] ]
|
||||
[ Css.display Css.block ]
|
||||
]
|
||||
|
||||
|
||||
homeSectionStyles : Css.Style
|
||||
homeSectionStyles =
|
||||
Css.paddingTop (Css.px 10)
|
||||
|
||||
|
||||
homeStyles : Css.Style
|
||||
homeStyles =
|
||||
Css.batch
|
||||
[ Css.color (Css.hex "c92a60")
|
||||
, Css.active [ Css.color (Css.hex "c92a60") ]
|
||||
, Css.focus [ Css.color (Css.hex "c92a60") ]
|
||||
, Css.hover [ Css.color (Css.hex "9d2a60") ]
|
||||
, Css.visited
|
||||
[ Css.color (Css.hex "c92a60")
|
||||
, Css.hover [ Css.color (Css.hex "9d2a60") ]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
view : String -> Model -> Html Msg
|
||||
view origin model =
|
||||
let
|
||||
dogLink =
|
||||
origin ++ "/link/" ++ model.uuid
|
||||
in
|
||||
Html.div []
|
||||
[ Html.h3 [] [ text "Doglink created!" ]
|
||||
, Html.div []
|
||||
[ Html.input [ Attr.type_ "text", Attr.id "copy-doglink", Attr.value dogLink ] []
|
||||
, Html.a [ Attr.href "#", Events.onClick ClickedCopy ]
|
||||
div []
|
||||
[ h3 [ Attr.css [ headerStyles ] ] [ text "Doglink created!" ]
|
||||
, p [ Attr.css [ descriptionStyles ] ]
|
||||
[ text "Here's your Doglink! You can copy this and paste it wherever you might find useful. Maybe on twitter! I'm not your boss." ]
|
||||
, div [ Attr.css [ copyLineStyles ] ]
|
||||
[ Input.text [ Attr.id "copy-doglink", Attr.value dogLink ] []
|
||||
, Input.copyButton [ Events.onClick ClickedCopy ]
|
||||
[ if model.copied then
|
||||
text "Copied!"
|
||||
|
||||
|
@ -40,8 +84,8 @@ view origin model =
|
|||
text "Copy"
|
||||
]
|
||||
]
|
||||
, Html.div []
|
||||
[ Html.a [ Attr.href "/" ] [ text "Return Home" ]
|
||||
, div [ Attr.css [ homeSectionStyles ] ]
|
||||
[ a [ Attr.css [ homeStyles ], Attr.href "/" ] [ text "Return Home" ]
|
||||
]
|
||||
]
|
||||
|
||||
|
@ -52,11 +96,11 @@ type Msg
|
|||
| ExpiredCopy
|
||||
|
||||
|
||||
update : Nav.Key -> Msg -> Model -> ( Model, Cmd Msg )
|
||||
update key msg model =
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
ClickedCopy ->
|
||||
( model, copyLink ())
|
||||
( model, copyLink () )
|
||||
|
||||
LinkCopied ->
|
||||
( { model | copied = True }, expireCopy )
|
||||
|
|
242
server/Cargo.lock
generated
242
server/Cargo.lock
generated
|
@ -42,6 +42,18 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-governor"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fbf4afa1e2f7c28040febe2a7199ad0a5fed564dd645da06ab12642c7d22483"
|
||||
dependencies = [
|
||||
"actix-http",
|
||||
"actix-web",
|
||||
"futures",
|
||||
"governor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "actix-http"
|
||||
version = "3.2.2"
|
||||
|
@ -389,6 +401,12 @@ dependencies = [
|
|||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.11.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "572f695136211188308f16ad2ca5c851a712c464060ae6974944458eb83880ba"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
|
@ -514,6 +532,19 @@ dependencies = [
|
|||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dashmap"
|
||||
version = "5.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "907076dfda823b0b36d2a1bb5f90c96660a5bbcd7729e10727f07858f22c4edc"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"hashbrown",
|
||||
"lock_api",
|
||||
"once_cell",
|
||||
"parking_lot_core 0.9.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "derive_more"
|
||||
version = "0.99.17"
|
||||
|
@ -541,6 +572,7 @@ dependencies = [
|
|||
name = "doglinks"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"actix-governor",
|
||||
"actix-web",
|
||||
"actix-web-lab",
|
||||
"serde",
|
||||
|
@ -600,12 +632,65 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38390104763dc37a5145a53c29c63c1290b5d316d6086ec32c293f6736051bb0"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-executor",
|
||||
"futures-io",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-channel"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52ba265a92256105f45b719605a571ffe2d1f0fea3807304b522c1d778f79eed"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-sink",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-core"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "04909a7a7e4633ae6c4a9ab280aeb86da1236243a77b694a49eacd659a4bd3ac"
|
||||
|
||||
[[package]]
|
||||
name = "futures-executor"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7acc85df6714c176ab5edf386123fafe217be88c0840ec11f199441134a074e2"
|
||||
dependencies = [
|
||||
"futures-core",
|
||||
"futures-task",
|
||||
"futures-util",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-io"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "00f5fb52a06bdcadeb54e8d3671f8888a39697dcb0b81b23b55174030427f4eb"
|
||||
|
||||
[[package]]
|
||||
name = "futures-macro"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bdfb8ce053d86b91919aad980c220b1fb8401a9394410e1c289ed7e66b61835d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "futures-sink"
|
||||
version = "0.3.25"
|
||||
|
@ -618,14 +703,25 @@ version = "0.3.25"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ffb393ac5d9a6eaa9d3fdf37ae2776656b706e200c8e16b1bdb227f5198e6ea"
|
||||
|
||||
[[package]]
|
||||
name = "futures-timer"
|
||||
version = "3.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e64b03909df88034c26dc1547e8970b91f98bdb65165d6a4e9110d94263dbb2c"
|
||||
|
||||
[[package]]
|
||||
name = "futures-util"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "197676987abd2f9cadff84926f410af1c183608d36641465df73ae8211dc65d6"
|
||||
dependencies = [
|
||||
"futures-channel",
|
||||
"futures-core",
|
||||
"futures-io",
|
||||
"futures-macro",
|
||||
"futures-sink",
|
||||
"futures-task",
|
||||
"memchr",
|
||||
"pin-project-lite",
|
||||
"pin-utils",
|
||||
"slab",
|
||||
|
@ -658,7 +754,24 @@ checksum = "c05aeb6a22b8f62540c194aac980f2115af067bfe15a0734d7277a768d396b31"
|
|||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "governor"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "19775995ee20209163239355bc3ad2f33f83da35d9ef72dea26e5af753552c87"
|
||||
dependencies = [
|
||||
"dashmap",
|
||||
"futures",
|
||||
"futures-timer",
|
||||
"no-std-compat",
|
||||
"nonzero_ext",
|
||||
"parking_lot 0.12.1",
|
||||
"quanta",
|
||||
"rand",
|
||||
"smallvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -789,6 +902,15 @@ dependencies = [
|
|||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49409df3e3bf0856b916e2ceaca09ee28e6871cf7d9ce97a692cacfdb2a25a47"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "language-tags"
|
||||
version = "0.3.2"
|
||||
|
@ -844,6 +966,15 @@ dependencies = [
|
|||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mach"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b823e83b2affd8f40a9ee8c29dbc56404c1e34cd2710921f2801e2cf29527afa"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mediatype"
|
||||
version = "0.19.11"
|
||||
|
@ -898,10 +1029,22 @@ checksum = "e5d732bc30207a6423068df043e3d02e0735b155ad7ce1a6f76fe2baa5b158de"
|
|||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"wasi",
|
||||
"wasi 0.11.0+wasi-snapshot-preview1",
|
||||
"windows-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "no-std-compat"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b93853da6d84c2e3c7d730d6473e8817692dd89be387eb01b94d7f108ecb5b8c"
|
||||
|
||||
[[package]]
|
||||
name = "nonzero_ext"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38bf9645c8b145698bb0b18a4637dcacbc421ea49bef2317e4fd8065a387cf21"
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.14.0"
|
||||
|
@ -1005,6 +1148,22 @@ dependencies = [
|
|||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quanta"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "20afe714292d5e879d8b12740aa223c6a88f118af41870e8b6196e39a02238a8"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
"libc",
|
||||
"mach",
|
||||
"once_cell",
|
||||
"raw-cpuid",
|
||||
"wasi 0.10.2+wasi-snapshot-preview1",
|
||||
"web-sys",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.23"
|
||||
|
@ -1044,6 +1203,15 @@ dependencies = [
|
|||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-cpuid"
|
||||
version = "10.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a6823ea29436221176fe662da99998ad3b4db2c7f31e7b6f5fe43adccd6320bb"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.16"
|
||||
|
@ -1414,12 +1582,82 @@ version = "0.9.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.10.2+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasi"
|
||||
version = "0.11.0+wasi-snapshot-preview1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "eaf9f5aceeec8be17c128b2e93e031fb8a4d469bb9c4ae2d7dc1888b26887268"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4c8ffb332579b0557b52d268b91feab8df3615f265d5270fec2a8c95b17c1142"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"log",
|
||||
"once_cell",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "052be0f94026e6cbc75cdefc9bae13fd6052cdcaf532fa6c45e7ae33a1e6c810"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"wasm-bindgen-backend",
|
||||
"wasm-bindgen-shared",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1c38c045535d93ec4f0b4defec448e4291638ee608530863b1e2ba115d4fff7f"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.60"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcda906d8be16e728fd5adc5b729afad4e444e106ab28cd1c7256e54fa61510f"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
|
|
|
@ -6,6 +6,7 @@ edition = "2021"
|
|||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
actix-governor = "0.3.2"
|
||||
actix-web = "4"
|
||||
actix-web-lab = { version = "0.18.8", features = ["spa"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
use std::time::Duration;
|
||||
|
||||
use actix_governor::{GlobalKeyExtractor, Governor, GovernorConfigBuilder};
|
||||
use actix_web::{
|
||||
body::MessageBody,
|
||||
dev::{ServiceRequest, ServiceResponse},
|
||||
error::{ErrorBadRequest, ErrorInternalServerError},
|
||||
guard,
|
||||
http::header::LOCATION,
|
||||
web::{self, Json, Path},
|
||||
App, HttpResponse, HttpServer,
|
||||
|
@ -10,8 +14,7 @@ use actix_web_lab::{
|
|||
middleware::{from_fn, Next},
|
||||
web::spa,
|
||||
};
|
||||
use sled::{CompareAndSwapError, Config, Db, Tree};
|
||||
use std::collections::BTreeSet;
|
||||
use sled::{Config, Db, Tree};
|
||||
use url::Url;
|
||||
use uuid::Uuid;
|
||||
|
||||
|
@ -32,8 +35,6 @@ impl State {
|
|||
}
|
||||
}
|
||||
|
||||
type StateValue = BTreeSet<Uuid>;
|
||||
|
||||
impl State {
|
||||
async fn save_link(&self, link: Url) -> sled::Result<Uuid> {
|
||||
let link_tree = self.link_tree.clone();
|
||||
|
@ -44,29 +45,13 @@ impl State {
|
|||
|
||||
uuid_tree.insert(uuid.as_bytes(), link.as_str())?;
|
||||
|
||||
let mut old = link_tree.get(link.as_str())?;
|
||||
let mut url_key = Vec::from(link.as_str().as_bytes());
|
||||
url_key.push(0);
|
||||
url_key.extend_from_slice(uuid.as_bytes());
|
||||
|
||||
loop {
|
||||
let new = old
|
||||
.clone()
|
||||
.and_then(|ivec| serde_json::from_slice(&ivec).ok())
|
||||
.or_else(|| Some(StateValue::new()))
|
||||
.map(|mut bts: StateValue| {
|
||||
bts.insert(uuid);
|
||||
bts
|
||||
})
|
||||
.and_then(|bts| serde_json::to_vec(&bts).ok());
|
||||
link_tree.insert(url_key, uuid.as_bytes())?;
|
||||
|
||||
match link_tree.compare_and_swap(link.to_string(), old, new)? {
|
||||
Ok(_) => return Ok(uuid),
|
||||
Err(CompareAndSwapError {
|
||||
current,
|
||||
proposed: _,
|
||||
}) => {
|
||||
old = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(uuid)
|
||||
})
|
||||
.await
|
||||
.expect("db panicked")
|
||||
|
@ -162,12 +147,37 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
|
||||
let state = State::build(db)?;
|
||||
|
||||
let limiter_config = GovernorConfigBuilder::const_default()
|
||||
.const_period(Duration::from_secs(10))
|
||||
.const_burst_size(1)
|
||||
.use_headers()
|
||||
.finish()
|
||||
.expect("Created a valid rate limit config");
|
||||
|
||||
let general_config = GovernorConfigBuilder::default()
|
||||
.period(Duration::from_millis(500))
|
||||
.burst_size(4)
|
||||
.key_extractor(GlobalKeyExtractor)
|
||||
.use_headers()
|
||||
.finish()
|
||||
.expect("Created a valid rate limit config");
|
||||
|
||||
HttpServer::new(move || {
|
||||
App::new()
|
||||
.app_data(web::Data::new(state.clone()))
|
||||
.wrap(from_fn(deny_invalid_agents))
|
||||
.route("/link", web::post().to(new_link))
|
||||
.route("/link/{uuid}", web::get().to(link_to))
|
||||
.service(
|
||||
web::resource("/link")
|
||||
.guard(guard::Post())
|
||||
.wrap(Governor::new(&limiter_config))
|
||||
.wrap(Governor::new(&general_config))
|
||||
.route(web::post().to(new_link)),
|
||||
)
|
||||
.service(
|
||||
web::resource("/link/{uuid}")
|
||||
.guard(guard::Get())
|
||||
.route(web::get().to(link_to)),
|
||||
)
|
||||
.service(
|
||||
spa()
|
||||
.index_file("./static/index.html")
|
||||
|
|
|
@ -3,8 +3,21 @@
|
|||
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="width=device-width,initial-scale=1" name="viewport">
|
||||
<title>Doglinks</title>
|
||||
<script src="/static/main.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #333;
|
||||
color: #f5f5f5;
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
|
Loading…
Reference in a new issue