Skip to content
Permalink
Browse files

Remove all website frontend/templating stuff

I'm rethinking my whole thing, so, yeah.
The actual website will be rendered by a completely different app.
  • Loading branch information...
myfreeweb committed Feb 1, 2017
1 parent 9a6c418 commit 1bdf8e1469c7388e1e6468df1ad0bb62295d4024
@@ -1,5 +1,7 @@
# sweetroll [![unlicense](https://img.shields.io/badge/un-license-green.svg?style=flat)](http://unlicense.org)

** Big changes in progress! **

A website engine for [the indie web] with curved swords. *Curved! Swords!*

- uses [Git]+[JSON] for storage
@@ -139,21 +141,6 @@ $ http -f post localhost:3000/micropub "Authorization: Bearer $(cat token)" h=en

## TODO

- html/frontend/templating
- [ ] support [WebFinger](https://webfinger.net) with HTML as the source of truth + additional links from config e.g. for [remoteStorage](https://remotestorage.io)
- [ ] figure out URL/canonical/etc. handling for alternative networks & mirrors like .onion & IPFS -- including webmentions!
- [ ] custom non-entry html pages
- [ ] [archive](https://indieweb.org/archive) pages by month. see footer of anildash.com
- [ ] sitemap.xml
- [ ] indieweb-components: a component for a Medium-style popup on selection that offers a fragmention link and (?) indie-config repost-quote-something (look how [selection-sharer](https://github.com/xdamman/selection-sharer) works on mobile!! but probably should look the same just at the opposite direction than iOS's popup)
- event system
- [ ] real-time page updates with Server-Sent Events (make a Web Component that will show the update button)
- [ ] static mode: on these events, regenerate website pages into static HTML files (and serve them for better performance)
- plugin api
- [ ] cron-style scheduling
- [ ] post manipulation
- [ ] HTTP request (webhook) handling
- [ ] example plugin: Telegram bot (posting, webmention notifications, responding to them, deleting them, etc.)
- webmention
- [ ] reverify/refetch to update user profiles and stuff
- [ ] moderation tools
@@ -165,7 +152,6 @@ $ http -f post localhost:3000/micropub "Authorization: Bearer $(cat token)" h=en
- micropub
- [ ] check auth scopes
- [ ] indieweb-algorithms?: ensure the person you're replying to *never* gets picked up you when you're replying (caught in test without own h-card) (what?)
- [ ] tags? (kill the difference between categories and tags? // use symlinks to add to multiple categories/tags)
- [ ] extract a `WebPrelude` package: `Sweetroll.Prelude`, `Sweetroll.HTTPClient`, `formToObject`, more stuff

## License
@@ -9,15 +9,9 @@
"license": "Unlicense",
"private": true,
"dependencies": {
"webcomponentsjs": "~0.7.22",
"indieweb-components": "~0.1.2",
"lodash": "~4.15.0",
"moment": "~2.14.1",
"sanitize-css": "^4.1.0",
"normalize-opentype.css": "^0.2.4",
"svgxuse": "^1.1.21",
"Font-Awesome-SVG-PNG": "font-awesome-svg-png#^1.1.5",
"SparkMD5": "^2.0.2",
"lazyload-image": "^2.1.0"
"SparkMD5": "^2.0.2"
}
}

This file was deleted.

Oops, something went wrong.
@@ -5,176 +5,43 @@
module Sweetroll.Api where

import Sweetroll.Prelude hiding (Context)
import Data.Maybe (fromJust)
import qualified Data.HashMap.Strict as HMS
import Data.Microformats2.Parser.HtmlUtil (getInnerHtml)
import qualified Text.HTML.DOM as HTML
import Text.XML (documentRoot)
import qualified Network.HTTP.Link as L
import Network.Wai
import Network.Wai.UrlMap
import Network.Wai.Middleware.AcceptOverride
import Network.Wai.Middleware.Autohead
import Network.Wai.Middleware.Cors
import Network.Wai.Middleware.Gzip
import Network.Wai.Middleware.Throttle
import Network.Wai.Application.Static
import WaiAppStatic.Types
import Servant
import Gitson
import Gitson.Util (maybeReadIntString)
import Sweetroll.Conf
import Sweetroll.Monads
import Sweetroll.Routes
import Sweetroll.Pages
import Sweetroll.Slice
import Sweetroll.Rendering
import Sweetroll.Auth
import Sweetroll.Micropub.Endpoint
import Sweetroll.Webmention.Receive
import Sweetroll.Proxy

getIndieConfig Sweetroll IndieConfig
getIndieConfig = getConfOpt indieConfig

getBaseCss Sweetroll LByteString
getBaseCss = return baseCss

getDefaultCss Sweetroll LByteString
getDefaultCss = return defaultCss

getDefaultIcons Sweetroll LByteString
getDefaultIcons = return defaultIcons

getIndex Sweetroll (WithLink (View IndexedPage))
getIndex = do
ipp getConfOpt itemsPerPage
catNames getConfOpt categoriesInLanding
(slices, entries0) foldM (\(slices, entries) catName
readCategory ipp Nothing Nothing entries catName >>= \(slices', entries') return (slices ++ slices', entries'))
([], HMS.empty)
catNames
entries postprocessEntries entries0
selfLink genLink "self" $ permalink (Proxy Proxy IndexRoute)
addLinks [selfLink] $ mkView $ IndexedPage catNames slices entries

getCat String Maybe Int Maybe Int Sweetroll (WithLink (View IndexedPage))
getCat catName before after = do
ipp getConfOpt itemsPerPage
(slice : slices, entries0) readCategory ipp before after HMS.empty catName
entries postprocessEntries entries0
guardBoolM (renderError err404 "404") (not $ null $ sliceItems slice)
selfLink genLink "self" $ catLink slice
addLinks [selfLink] $ mkView $ IndexedPage [catName] (slice : slices) entries

getEntry String String Sweetroll (WithLink (View EntryPage))
getEntry catName slug = do
entry postprocessEntry =<< (guardJustM (notFoundOrGone catName slug) $ readDocumentByName catName slug)
-- TODO: cacheHTTPDate -- don't forget responses' dates! -- 204 will be thrown before rendering!
otherSlugs listDocumentKeys catName
selfLink genLink "self" $ permalink (Proxy Proxy EntryRoute) catName slug
addLinks [selfLink] $ mkView $ EntryPage catName (map readSlug $ sort otherSlugs) (slug, entry)

postprocessEntries HashMap α Value Sweetroll (HashMap α Value)
postprocessEntries = mapM postprocessEntry

postprocessEntry Value Sweetroll Value
postprocessEntry entry = do
secs getSecs
conf getConf
let proxifyLink s = proxiedUri secs s
proxifyHtml s = fromMaybe s $ getInnerHtml Nothing $
(proxyImages secs conf . detwitterizeEmoji) $ documentRoot $ HTML.parseSTChunks ["<div>", s, "</div>"]
ppr e = foldr ($) e [ (& key "properties" . key "photo" . values . _String %~ proxifyLink)
, (& key "properties" . key "content" . values . key "html" . _String %~ proxifyHtml) ]
return $ transform (\x if isJust (x ^? key "type") then ppr x else x) entry


sweetrollServerT ServerT SweetrollAPI Sweetroll
sweetrollServerT = getIndieConfig :<|> getBaseCss :<|> getDefaultCss :<|> getDefaultIcons
:<|> postLogin :<|> getAuth
sweetrollServerT = postLogin :<|> getAuth
:<|> postMedia :<|> postMicropub :<|> getMicropub
:<|> receiveWebmention
:<|> getEntry :<|> getCat :<|> getIndex

sweetrollApp WaiThrottle SweetrollCtx Application
sweetrollApp thr ctx =
simpleCors
$ throttle defThr { isThrottled = (\r return $ requestMethod r /= "GET" && requestMethod r /= "HEAD")
, throttleBurst = 5 } thr
$ autohead
$ cacheControlAuto
$ acceptOverride
$ gzip def
$ supportFormAuth
$ mapUrls $ mount "bower" (staticApp $ (embeddedSettings bowerComponents) { ssMaxAge = MaxAgeSeconds 30 })
<|> mount "static" (staticApp $ (defaultWebAppSettings "static") { ssMaxAge = MaxAgeSeconds 30 })
<|> mount "proxy" (requestProxy ctx)
$ mapUrls $ mount "proxy" (requestProxy ctx)
<|> mountRoot (serveWithContext sweetrollAPI sweetrollContext $ sweetrollServer ctx)
where sweetrollServer c = enter (sweetrollToExcept c) sweetrollServerT
sweetrollContext = authHandler (secretKey $ _ctxSecs ctx) :. EmptyContext
defThr = defaultThrottleSettings

cacheControlAuto Middleware
cacheControlAuto app req respond = app req $ respond . setCC
where ver = join $ lookup "_v" $ queryString req
setCC resp = if isJust ver
then mapResponseHeaders (insertMap hCacheControl "public, max-age=31536000, immutable") resp
else resp

initSweetrollApp SweetrollConf SweetrollSecrets IO Application
initSweetrollApp conf secs = do
thr initThrottler
fmap (sweetrollApp thr) $ initCtx conf secs


genLink MonadSweetroll μ Text URI μ L.Link
genLink rel u = do
base getConfOpt baseURI
return $ L.Link (u `relativeTo` base) [(L.Rel, rel)]

addLinks (MonadSweetroll μ, AddHeader "Link" [L.Link] α β) [L.Link] μ α μ β
addLinks ls a = do
webmention genLink "webmention" $ permalink (Proxy Proxy PostWebmentionRoute)
micropub genLink "micropub" $ permalink (Proxy Proxy PostMicropubRoute)
tokenEndpoint genLink "token_endpoint" $ permalink (Proxy Proxy PostLoginRoute)
authorizationEndpoint getConfOpt indieAuthRedirEndpoint >>= \x return $ fromJust $ L.lnk x [(L.Rel, "authorization_endpoint")]
hub getConfOpt pushHub >>= \x return $ fromJust $ L.lnk x [(L.Rel, "hub")]
return . addHeader (webmention : micropub : tokenEndpoint : authorizationEndpoint : hub : ls) =<< a



readSlug String EntrySlug
readSlug x = drop 1 $ fromMaybe "-404" $ snd <$> maybeReadIntString x -- errors should never happen

readEntry MonadIO μ CategoryName EntrySlug μ (Maybe (EntrySlug, Value))
readEntry category slug = liftIO $ do
doc readDocumentByName category slug IO (Maybe Value)
return $ (slug, ) <$> doc

readCategory Int Maybe Int Maybe Int HashMap String Value CategoryName
Sweetroll ([Slice String], HashMap String Value)
readCategory perPage before after initialEntries catsName =
foldM readCategory' ([], initialEntries) $ splitOn "+" catsName
where readCategory' (prevSlices, prevEntries) catName = do
ks listDocumentKeys catName
let newSlice = sliceCategory perPage before after catName $ map (second $ drop 1) $ mapMaybe maybeReadIntString ks
newSlices = case prevSlices of
(s : ss) mergeSlices perPage (isJust after) newSlice s : newSlice : s : ss
[] [newSlice]
newEntries foldM (\entries u
if u `member` entries then return entries else do
entry fmap snd (parseEntryURIRelative $ fromJust $ parseURIReference u) >>= readEntry catName -- XXX: eliminate URI
return $ case entry of
Just (_, v) insertMap u v entries
Nothing entries)
prevEntries
(map snd $ sliceItems newSlice)
return (newSlices, newEntries)

notFoundOrGone (MonadIO μ, MonadSweetroll μ) CategoryName EntrySlug μ ServantErr
notFoundOrGone catName slug = do
deleted liftIO . readTVarIO =<< getDeleted
if any (\x catName `isPrefixOf` x && (slug ++ ".json") `isSuffixOf` x) deleted
then renderError err410 "410"
else renderError err404 "404"
@@ -8,19 +8,9 @@ module Sweetroll.Conf (

import Sweetroll.Prelude
import CMark
import CMark.Highlight
import qualified Data.HashMap.Strict as HMS
import Data.Aeson.TH
import Data.Microformats2.Parser
import Data.FileEmbed

newtype IndieConfig = MkIndieConfig Value

instance ToJSON IndieConfig where
toJSON (MkIndieConfig v) = toJSON v

instance FromJSON IndieConfig where
parseJSON v = return $ MkIndieConfig v

newtype SyndicationConfig = MkSyndicationConfig Value

@@ -42,7 +32,6 @@ data SweetrollConf = SweetrollConf
, categoriesInLanding Maybe [String]
, categoriesInNav Maybe [String]
, categoryTitles Maybe (HashMap Text Text)
, indieConfig Maybe IndieConfig
, syndicationConfig Maybe SyndicationConfig
, mediaEndpoint Maybe String
, indieAuthRedirEndpoint Maybe String
@@ -66,11 +55,6 @@ instance Default SweetrollConf where
, ("articles", "Articles")
, ("replies+likes", "Responses")
, ("bookmarks", "Bookmarks") ]
, indieConfig = Just $ MkIndieConfig $ object [
"reply" .= asText "https://quill.p3k.io/new?reply={url}"
, "bookmark" .= asText "https://quill.p3k.io/bookmark?url={url}"
, "like" .= asText "https://quill.p3k.io/favorite?url={url}"
, "repost" .= asText "https://quill.p3k.io/repost?url={url}" ]
, syndicationConfig = Just $ MkSyndicationConfig $ toJSON [
object [ "name" .= asText "twitter.com", "uid" .= asText "<a href=\"https://brid.gy/publish/twitter\" data-synd></a>" ]
, object [ "name" .= asText "facebook.com", "uid" .= asText "<a href=\"https://brid.gy/publish/facebook\" data-synd></a>" ]
@@ -99,22 +83,3 @@ mf2Options = def

cmarkOptions [CMarkOption]
cmarkOptions = [ optNormalize, optSmart ]

bowerComponents [(FilePath, ByteString)]
bowerComponents = [ ("webcomponentsjs/webcomponents-lite.min.js", $(embedFile "bower_components/webcomponentsjs/webcomponents-lite.min.js"))
, ("lazyload-image/lazyload-image.html", $(embedFile "bower_components/lazyload-image/lazyload-image.html"))
, ("indieweb-components/indie-action.html", $(embedFile "bower_components/indieweb-components/indie-action.html"))
, ("indieweb-components/fragmention-target.html", $(embedFile "bower_components/indieweb-components/fragmention-target.html"))
, ("findAndReplaceDOMText/src/findAndReplaceDOMText.js", $(embedFile "bower_components/findAndReplaceDOMText/src/findAndReplaceDOMText.js"))
, ("svgxuse/svgxuse.js", $(embedFile "bower_components/svgxuse/svgxuse.js")) ]

baseCss LByteString
baseCss = sanitizeCss ++ opentypeCss
where sanitizeCss = cs $(embedFile "bower_components/sanitize-css/sanitize.css")
opentypeCss = cs $(embedFile "bower_components/normalize-opentype.css/normalize-opentype.css")

defaultCss LByteString
defaultCss = cs (styleToCss tango) ++ cs $(embedFile "style.css")

defaultIcons LByteString
defaultIcons = cs $(embedFile "icons.svg")
@@ -6,10 +6,10 @@ module Sweetroll.Events where

import Sweetroll.Prelude
import Sweetroll.Conf
import Sweetroll.Routes
import Sweetroll.Monads
import Sweetroll.Webmention.Send
import Sweetroll.HTTPClient
import Data.Maybe (fromJust)

type MonadSweetrollEvent μ = (MonadIO μ, MonadBaseControl IO μ, MonadCatch μ, MonadSweetroll μ)

@@ -53,14 +53,9 @@ onPostUndeleted category slug absUrl obj = do

notifyPuSHCategory (MonadSweetrollEvent μ) String μ ()
notifyPuSHCategory catName = do
notifyPuSH $ permalink (Proxy Proxy IndexRoute)
notifyPuSH $ permalink (Proxy Proxy CatRoute) catName Nothing Nothing
notifyPuSH $ atomizeUri $ permalink (Proxy Proxy CatRoute) catName Nothing Nothing
-- XXX: should send to unnamed combinations too
catsToNotify liftM (filter (cs catName `isInfixOf`) . keys) $ getConfOpt categoryTitles
forM_ catsToNotify $ \c do
notifyPuSH $ permalink (Proxy Proxy CatRoute) (cs c) Nothing Nothing
notifyPuSH $ atomizeUri $ permalink (Proxy Proxy CatRoute) (cs c) Nothing Nothing
notifyPuSH $ fromJust $ parseURI "/"
notifyPuSH $ fromJust $ parseURI $ "/" ++ catName
-- XXX

notifyPuSH (MonadSweetrollEvent μ) URI μ ()
notifyPuSH l = do
Oops, something went wrong.

0 comments on commit 1bdf8e1

Please sign in to comment.
You can’t perform that action at this time.