forked from glotcode/glot-www
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Snippet.hs
161 lines (137 loc) · 6.5 KB
/
Snippet.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
module Handler.Snippet where
import Import hiding (pack)
import Widget.Editor (editorWidget, footerWidget)
import Widget.Share (shareWidget)
import Util.Handler (titleConcat, urlDecode')
import Util.Alert (successHtml)
import Text.Hamlet (hamletFile)
import qualified Util.Snippet as Snippet
import qualified Data.Text.Encoding as Encoding
import qualified Data.Text.Encoding.Error as Encoding.Error
import qualified Glot.Snippet
import qualified Data.Aeson as Aeson
import qualified Data.List.NonEmpty as NonEmpty
import qualified Network.Wai as Wai
import qualified Util.Handler as Handler
import qualified Glot.Language as Language
import Data.Function ((&))
getSnippetR :: Text -> Handler Html
getSnippetR slug = do
mUserId <- maybeAuthId
(snippet, files, profile, runParams, runResult) <- runDB $ do
Entity snippetId snippet <- getBy404 $ UniqueCodeSnippetSlug slug
files <- selectList [CodeFileCodeSnippetId ==. snippetId] [Asc CodeFileId]
profile <- maybe (pure Nothing) (getBy . UniqueProfile) (codeSnippetUserId snippet)
runParams <- getBy $ UniqueRunParams slug
-- TODO: fix
runResult <- pure Nothing -- getBy $ UniqueRunResultHash slug $ (snippetHash snippet $ formatRunParams runParams)
pure (snippet, map entityVal files, profile, runParams, runResult)
language <- Handler.getLanguage (codeSnippetLanguage snippet)
let userIsSnippetOwner = mUserId == codeSnippetUserId snippet
defaultLayout $ do
setTitle $ titleConcat [Snippet.title snippet, " - ", Language.name language, " Snippet"]
setDescription (snippetDescription language)
Handler.setCanonicalUrl (SnippetR slug)
toWidgetHead $(hamletFile "templates/snippet/opengraph.hamlet")
toWidgetHead $(hamletFile "templates/snippet/twitter-card.hamlet")
$(widgetFile "snippet")
snippetDescription :: Language.Language -> Text
snippetDescription language =
if Language.isRunnable language then
concat ["Run this ", Language.name language, " code snippet in the browser."]
else
concat [Language.name language, " snippet"]
putSnippetR :: Text -> Handler Value
putSnippetR snippetSlug = do
langVersion <- fromMaybe "latest" <$> lookupGetParam "version"
runCommand <- urlDecode' <$> fromMaybe "" <$> lookupGetParam "command"
stdinData <- urlDecode' <$> fromMaybe "" <$> lookupGetParam "stdin"
req <- reqWaiRequest <$> getRequest
body <- liftIO $ Wai.strictRequestBody req
now <- liftIO getCurrentTime
maybeUserId <- maybeAuthId
case Aeson.eitherDecode' body of
Left err ->
sendResponseStatus status400 $ object ["message" .= ("Invalid request body: " <> err)]
Right payload -> do
runDB $ do
Entity snippetId oldSnippet <- getBy404 (UniqueCodeSnippetSlug snippetSlug)
lift $ ensureSnippetOwner maybeUserId oldSnippet
let snippet = Glot.Snippet.toCodeSnippet snippetSlug (codeSnippetCreated oldSnippet) now maybeUserId payload
replace snippetId snippet
deleteWhere [ CodeFileCodeSnippetId ==. snippetId ]
insertMany_ (map (Glot.Snippet.toCodeFile snippetId) (NonEmpty.toList $ Glot.Snippet.files payload))
Snippet.persistRunParams Snippet.RunParameters{..}
pure ()
setMessage $ successHtml "Updated snippet"
pure $ object []
deleteSnippetR :: Text -> Handler Value
deleteSnippetR slug = do
maybeUserId <- maybeAuthId
runDB $ do
Entity snippetId snippet <- getBy404 $ UniqueCodeSnippetSlug slug
lift $ ensureSnippetOwner maybeUserId snippet
deleteWhere [ CodeFileCodeSnippetId ==. snippetId ]
delete snippetId
pure ()
pure $ object []
ensureSnippetOwner :: Maybe UserId -> CodeSnippet -> Handler ()
ensureSnippetOwner maybeUserId CodeSnippet{..} =
case (maybeUserId, codeSnippetUserId) of
(Just userId, Just snippetUserId) ->
if userId == snippetUserId then
pure ()
else
sendResponseStatus status403 $
object [ "error" .= ("You are not the owner of this snippet" :: Text) ]
_ ->
sendResponseStatus status403 $
object [ "error" .= ("You are not the owner of this snippet" :: Text) ]
getSnippetEmbedR :: Text -> Handler Html
getSnippetEmbedR slug = do
(snippet, files, profile, runParams) <- runDB $ do
Entity snippetId snippet <- getBy404 $ UniqueCodeSnippetSlug slug
files <- selectList [CodeFileCodeSnippetId ==. snippetId] [Asc CodeFileId]
profile <- maybe (pure Nothing) (getBy . UniqueProfile) (codeSnippetUserId snippet)
runParams <- getBy $ UniqueRunParams slug
pure (snippet, map entityVal files, profile, runParams)
language <- Handler.getLanguage (codeSnippetLanguage snippet)
defaultLayout $ do
setTitle $ titleConcat [Snippet.title snippet, " - ", Language.name language, " Snippet"]
Handler.setCanonicalUrl (SnippetEmbedR slug)
$(widgetFile "snippet/embed")
getSnippetRawR :: Text -> Handler Html
getSnippetRawR slug = do
(snippet, files) <- runDB $ do
Entity snippetId snippet <- getBy404 $ UniqueCodeSnippetSlug slug
files <- selectList [CodeFileCodeSnippetId ==. snippetId] [Asc CodeFileId]
pure (snippet, map entityVal files)
case files of
[file] ->
codeFileName file
& SnippetRawFileR slug
& redirect
_ -> do
language <- Handler.getLanguage (codeSnippetLanguage snippet)
defaultLayout $ do
setTitle $ titleConcat [Snippet.title snippet, " - ", Language.name language, " Snippet"]
Handler.setCanonicalUrl (SnippetRawR slug)
$(widgetFile "snippet/raw")
getSnippetRawFileR :: Text -> Text -> Handler Text
getSnippetRawFileR slug filename = do
files <- runDB $ do
Entity snippetId _ <- getBy404 $ UniqueCodeSnippetSlug slug
files <- selectList [CodeFileCodeSnippetId ==. snippetId] [Asc CodeFileId]
pure (map entityVal files)
case findFileWithFilename files filename of
Just file ->
codeFileContent file
& Encoding.decodeUtf8With Encoding.Error.lenientDecode
& pure
Nothing ->
notFound
findFileWithFilename :: [CodeFile] -> Text -> Maybe CodeFile
findFileWithFilename files filename =
files
& filter (\f -> codeFileName f == filename)
& listToMaybe