Initial commit

This commit is contained in:
Cale Gibbard 2023-02-07 13:16:26 -05:00
commit 2bfa91eca1
33 changed files with 409 additions and 0 deletions

16
.gitignore vendored Normal file
View File

@ -0,0 +1,16 @@
.attr-cache
.cabal-sandbox
*.hi
*.o
cabal.project.local
cabal.sandbox.config
ctags
dist-newstyle/
dist/
ghcid-output.txt
profile/
result
result-*
tags
TAGS
static.out

View File

@ -0,0 +1,2 @@
# DO NOT HAND-EDIT THIS FILE
import (import ./thunk.nix)

View File

@ -0,0 +1,8 @@
{
"owner": "obsidiansystems",
"repo": "obelisk",
"branch": "cg/user-nixpkgs-overlays",
"private": false,
"rev": "c1c1e7e0aedefdf39497f4dda91cda76df1a05a9",
"sha256": "1xsp20j12sg6mg9wl640b709ksl3zs3bbkylm559gyvfbvll74p6"
}

12
.obelisk/impl/thunk.nix Normal file
View File

@ -0,0 +1,12 @@
# DO NOT HAND-EDIT THIS FILE
let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
if !fetchSubmodules && !private then builtins.fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
} else (import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz";
sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr";
}) {}).fetchFromGitHub {
inherit owner repo rev sha256 fetchSubmodules private;
};
json = builtins.fromJSON (builtins.readFile ./github.json);
in fetch json

6
README.md Normal file
View File

@ -0,0 +1,6 @@
# This repo
This is an example showing how to override a nixpkgs package, as well as include a Haskell package dependency not on Hackage.
See primarily the overrides in default.nix.

29
backend/backend.cabal Normal file
View File

@ -0,0 +1,29 @@
name: backend
version: 0.1
cabal-version: >= 1.8
build-type: Simple
library
hs-source-dirs: src
if impl(ghcjs)
buildable: False
build-depends: base
, common
, frontend
, obelisk-backend
, obelisk-route
exposed-modules:
Backend
ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -fno-show-valid-hole-fits
executable backend
main-is: main.hs
hs-source-dirs: src-bin
ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -threaded -fno-show-valid-hole-fits
if impl(ghcjs)
buildable: False
build-depends: base
, backend
, common
, frontend
, obelisk-backend

1
backend/frontend.jsexe Symbolic link
View File

@ -0,0 +1 @@
../frontend-js/bin/frontend.jsexe

View File

@ -0,0 +1 @@
../../frontend-js/bin/frontend.jsexe

6
backend/src-bin/main.hs Normal file
View File

@ -0,0 +1,6 @@
import Backend
import Frontend
import Obelisk.Backend
main :: IO ()
main = runBackend backend frontend

10
backend/src/Backend.hs Normal file
View File

@ -0,0 +1,10 @@
module Backend where
import Common.Route
import Obelisk.Backend
backend :: Backend BackendRoute FrontendRoute
backend = Backend
{ _backend_run = \serve -> serve $ const $ return ()
, _backend_routeEncoder = fullRouteEncoder
}

1
backend/static Symbolic link
View File

@ -0,0 +1 @@
../static

3
cabal.project Normal file
View File

@ -0,0 +1,3 @@
optional-packages:
*
write-ghc-environment-files: never

15
common/common.cabal Normal file
View File

@ -0,0 +1,15 @@
name: common
version: 0.1
cabal-version: >= 1.2
build-type: Simple
library
hs-source-dirs: src
build-depends: base
, obelisk-route
, mtl
, text
exposed-modules:
Common.Api
Common.Route
ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -fno-show-valid-hole-fits

4
common/src/Common/Api.hs Normal file
View File

@ -0,0 +1,4 @@
module Common.Api where
commonStuff :: String
commonStuff = "Here is a string defined in Common.Api"

View File

@ -0,0 +1,48 @@
{-# LANGUAGE ConstraintKinds #-}
{-# LANGUAGE EmptyCase #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE KindSignatures #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE RankNTypes #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeFamilies #-}
module Common.Route where
{- -- You will probably want these imports for composing Encoders.
import Prelude hiding (id, (.))
import Control.Category
-}
import Data.Text (Text)
import Data.Functor.Identity
import Obelisk.Route
import Obelisk.Route.TH
data BackendRoute :: * -> * where
-- | Used to handle unparseable routes.
BackendRoute_Missing :: BackendRoute ()
-- You can define any routes that will be handled specially by the backend here.
-- i.e. These do not serve the frontend, but do something different, such as serving static files.
data FrontendRoute :: * -> * where
FrontendRoute_Main :: FrontendRoute ()
-- This type is used to define frontend routes, i.e. ones for which the backend will serve the frontend.
fullRouteEncoder
:: Encoder (Either Text) Identity (R (FullRoute BackendRoute FrontendRoute)) PageName
fullRouteEncoder = mkFullRouteEncoder
(FullRoute_Backend BackendRoute_Missing :/ ())
(\case
BackendRoute_Missing -> PathSegment "missing" $ unitEncoder mempty)
(\case
FrontendRoute_Main -> PathEnd $ unitEncoder mempty)
concat <$> mapM deriveRouteComponent
[ ''BackendRoute
, ''FrontendRoute
]

1
config/common/example Normal file
View File

@ -0,0 +1 @@
This string comes from config/common/example

1
config/common/route Normal file
View File

@ -0,0 +1 @@
http://localhost:8000

9
config/readme.md Normal file
View File

@ -0,0 +1,9 @@
### Config
Obelisk projects should contain a config folder with the following subfolders: common, frontend, and backend.
Things that should never be transmitted to the frontend belong in backend/ (e.g., email credentials)
Frontend-only configuration belongs in frontend/.
Shared configuration files (e.g., the route config) belong in common/

70
default.nix Normal file
View File

@ -0,0 +1,70 @@
{ system ? builtins.currentSystem
, nix-thunk ? import ./dep/nix-thunk {}
, obelisk ? import ./.obelisk/impl {
inherit system;
iosSdkVersion = "13.2";
# This argument (which is new, but was easy to add to Obelisk, thankfully), allows one to specify nixpkgs overlays.
# We'll use it to ensure we have the appropriate version of botan.
nixpkgsOverlays =
[
(self: super: rec {
botan2 =
(super.botan2.overrideAttrs (old: {
# Get rid of some patches that nixpkgs was applying to botan2 and which didn't apply to the branch.
patches = [];
# The --with-openssl flag didn't work for some reason, might need further figuring out.
configurePhase = builtins.replaceStrings [ "--with-openssl" ] [ " " ] old.configurePhase;
# Here, we use nix-thunk to get the source of the correct branch of the botan repo via a thunk.
# Nix thunks are essentially references to git repositories that can be unpacked to their
# source in-place when working on the project, or packed up into a few small files.
# After installing the nix-thunk command from https://github.com/obsidiansystems/nix-thunk
# you can run:
# nix-thunk unpack dep/botan
# from the top level of this repo to clone a copy of the botan git repo at the appropriate
# commit, and work on it from there. Similarly,
# nix-thunk pack dep/botan
# will pack it back up again, provided that the changes have been pushed somewhere.
# Note: there's a bug in the current version of Obelisk which occasionally gives it some trouble
# if certain repos are unpacked. If you have any trouble running an ob command
# (ob run, ob repl, etc.) with a thunk unpacked, try adding the flag --no-interpret dep
# and hopefully that will sort it out.
src = nix-thunk.thunkSource ./dep/botan;
})).override (old: {
# Also, turn on debugging.
extraConfigureFlags = "--debug-mode";
});
# For whatever reason, it seems callCabal2nix below wants to use the botan package rather than botan2.
# We could override the pkgconfigDepends of the resulting package, but this is easier.
botan = self.botan2;
})
];
# You must accept the Android Software Development Kit License Agreement at
# https://developer.android.com/studio/terms in order to build Android apps.
# Uncomment and set this to `true` to indicate your acceptance:
# config.android_sdk.accept_license = false;
# In order to use Let's Encrypt for HTTPS deployments you must accept
# their terms of service at https://letsencrypt.org/repository/.
# Uncomment and set this to `true` to indicate your acceptance:
# terms.security.acme.acceptTerms = false;
}
}:
with obelisk;
project ./. ({ pkgs, hackGet, ... }: {
android.applicationId = "systems.obsidian.obelisk.examples.minimal";
android.displayName = "Obelisk Minimal Example";
ios.bundleIdentifier = "systems.obsidian.obelisk.examples.minimal";
ios.bundleName = "Obelisk Minimal Example";
overrides = self: super:
with pkgs.haskell.lib; {
# Here, we get the tahoe-chk package from the chk.hs thunk, and use callCabal2nix to get a nix derivation to build it.
tahoe-chk = self.callCabal2nix "tahoe-chk" (nix-thunk.thunkSource ./dep/chkhs) {};
# We also ended up needing an override of the base32 library, which we obtain from Hackage.
base32 = self.callHackageDirect {
pkg = "base32";
ver = "0.2.2.0";
sha256 = "1qx7n2jyb9h1082434r90hfrjw5fab2j1yg0qzxh856fpksbh811";
};
};
})

2
dep/botan/default.nix Normal file
View File

@ -0,0 +1,2 @@
# DO NOT HAND-EDIT THIS FILE
import (import ./thunk.nix)

8
dep/botan/github.json Normal file
View File

@ -0,0 +1,8 @@
{
"owner": "PrivateStorageio",
"repo": "botan",
"branch": "3190.ffi-zfec",
"private": false,
"rev": "f9e20aae412f5b482334181885489a15bb8a4fd8",
"sha256": "0qcz9cxh0lymj09166wf9mxv9q2bhr3sllh71380dhq90ikaib7j"
}

12
dep/botan/thunk.nix Normal file
View File

@ -0,0 +1,12 @@
# DO NOT HAND-EDIT THIS FILE
let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
if !fetchSubmodules && !private then builtins.fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
} else (import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz";
sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr";
}) {}).fetchFromGitHub {
inherit owner repo rev sha256 fetchSubmodules private;
};
json = builtins.fromJSON (builtins.readFile ./github.json);
in fetch json

2
dep/chkhs/default.nix Normal file
View File

@ -0,0 +1,2 @@
# DO NOT HAND-EDIT THIS FILE
import (import ./thunk.nix)

8
dep/chkhs/git.json Normal file
View File

@ -0,0 +1,8 @@
{
"url": "https://gitlab.com/exarkun/chk.hs.git",
"rev": "46bdc9a1c235a26ab1cabdad7eceee36dccf4cdd",
"sha256": "0yykb9vyv0wci65nzq0aw0slw5njrzrbkrchpypd9vskvh7p8im0",
"private": false,
"fetchSubmodules": false,
"branch": "master"
}

17
dep/chkhs/thunk.nix Normal file
View File

@ -0,0 +1,17 @@
# DO NOT HAND-EDIT THIS FILE
let fetch = {url, rev, branch ? null, sha256 ? null, fetchSubmodules ? false, private ? false, ...}:
let realUrl = let firstChar = builtins.substring 0 1 url; in
if firstChar == "/" then /. + url
else if firstChar == "." then ./. + url
else url;
in if !fetchSubmodules && private then builtins.fetchGit {
url = realUrl; inherit rev;
${if branch == null then null else "ref"} = branch;
} else (import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz";
sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr";
}) {}).fetchgit {
url = realUrl; inherit rev sha256;
};
json = builtins.fromJSON (builtins.readFile ./git.json);
in fetch json

View File

@ -0,0 +1,2 @@
# DO NOT HAND-EDIT THIS FILE
import (import ./thunk.nix)

View File

@ -0,0 +1,7 @@
{
"owner": "obsidiansystems",
"repo": "nix-thunk",
"private": false,
"rev": "0982911d78fbd9932c3ed8104a930f313f49b69b",
"sha256": "130n8mzywbvyqd6aap6yk98zynq3r3kpj2hfzdym26plwz0rr3bj"
}

12
dep/nix-thunk/thunk.nix Normal file
View File

@ -0,0 +1,12 @@
# DO NOT HAND-EDIT THIS FILE
let fetch = { private ? false, fetchSubmodules ? false, owner, repo, rev, sha256, ... }:
if !fetchSubmodules && !private then builtins.fetchTarball {
url = "https://github.com/${owner}/${repo}/archive/${rev}.tar.gz"; inherit sha256;
} else (import (builtins.fetchTarball {
url = "https://github.com/NixOS/nixpkgs/archive/3aad50c30c826430b0270fcf8264c8c41b005403.tar.gz";
sha256 = "0xwqsf08sywd23x0xvw4c4ghq0l28w2ki22h0bdn766i16z9q2gr";
}) {}).fetchFromGitHub {
inherit owner repo rev sha256 fetchSubmodules private;
};
json = builtins.fromJSON (builtins.readFile ./github.json);
in fetch json

36
frontend/frontend.cabal Normal file
View File

@ -0,0 +1,36 @@
name: frontend
version: 0.1
cabal-version: >= 1.8
build-type: Simple
library
hs-source-dirs: src
build-depends: base
, common
, obelisk-frontend
, obelisk-route
, jsaddle
, reflex-dom-core
, obelisk-executable-config-lookup
, obelisk-generated-static
, text
exposed-modules:
Frontend
ghc-options: -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -O -fno-show-valid-hole-fits
executable frontend
main-is: main.hs
hs-source-dirs: src-bin
build-depends: base
, common
, obelisk-frontend
, obelisk-route
, reflex-dom
, obelisk-generated-static
, frontend
ghc-options: -threaded -O -Wall -Wredundant-constraints -Wincomplete-uni-patterns -Wincomplete-record-updates -fno-show-valid-hole-fits
if impl(ghcjs)
ghc-options: -dedupe
cpp-options: -DGHCJS_BROWSER
if os(darwin)
ghc-options: -dynamic

10
frontend/src-bin/main.hs Normal file
View File

@ -0,0 +1,10 @@
import Frontend
import Common.Route
import Obelisk.Frontend
import Obelisk.Route.Frontend
import Reflex.Dom
main :: IO ()
main = do
let Right validFullEncoder = checkEncoder fullRouteEncoder
run $ runFrontend validFullEncoder frontend

47
frontend/src/Frontend.hs Normal file
View File

@ -0,0 +1,47 @@
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TemplateHaskell #-}
module Frontend where
import Control.Monad
import qualified Data.Text as T
import qualified Data.Text.Encoding as T
import Language.Javascript.JSaddle (eval, liftJSM)
import Obelisk.Frontend
import Obelisk.Configs
import Obelisk.Route
import Obelisk.Generated.Static
import Reflex.Dom.Core
import Common.Api
import Common.Route
-- This runs in a monad that can be run on the client or the server.
-- To run code in a pure client or pure server context, use one of the
-- `prerender` functions.
frontend :: Frontend (R FrontendRoute)
frontend = Frontend
{ _frontend_head = do
el "title" $ text "Obelisk Minimal Example"
elAttr "link" ("href" =: $(static "main.css") <> "type" =: "text/css" <> "rel" =: "stylesheet") blank
, _frontend_body = do
el "h1" $ text "Welcome to Obelisk!"
el "p" $ text $ T.pack commonStuff
-- `prerender` and `prerender_` let you choose a widget to run on the server
-- during prerendering and a different widget to run on the client with
-- JavaScript. The following will generate a `blank` widget on the server and
-- print "Hello, World!" on the client.
prerender_ blank $ liftJSM $ void $ eval ("console.log('Hello, World!')" :: T.Text)
elAttr "img" ("src" =: $(static "obelisk.jpg")) blank
el "div" $ do
exampleConfig <- getConfig "common/example"
case exampleConfig of
Nothing -> text "No config file found in config/common/example"
Just s -> text $ T.decodeUtf8 s
return ()
}

3
static/main.css Normal file
View File

@ -0,0 +1,3 @@
p {
color: red;
}

BIN
static/obelisk.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB