does it work?
|
@ -39,6 +39,7 @@ library
|
|||
Handler.Common
|
||||
Handler.Home
|
||||
Handler.Station
|
||||
Handler.Ad
|
||||
Import
|
||||
Import.NoFoundation
|
||||
Model
|
||||
|
@ -72,6 +73,7 @@ library
|
|||
, persistent >=2.9 && <2.15
|
||||
, persistent-sqlite >=2.9 && <2.14
|
||||
, persistent-template >=2.5 && <2.14
|
||||
, random
|
||||
, safe
|
||||
, shakespeare >=2.0 && <2.2
|
||||
, template-haskell
|
||||
|
|
BIN
ads/image0.png
Normal file
After Width: | Height: | Size: 511 KiB |
BIN
ads/image1.png
Normal file
After Width: | Height: | Size: 1.7 MiB |
BIN
ads/image2.png
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
ads/image3.png
Normal file
After Width: | Height: | Size: 1.8 MiB |
BIN
ads/image4.png
Normal file
After Width: | Height: | Size: 440 KiB |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 15 KiB |
|
@ -15,6 +15,8 @@
|
|||
-- stationName/roundNumber
|
||||
/station/#Text/#Int StationRoundR GET POST
|
||||
|
||||
/ad/ AdR GET
|
||||
|
||||
|
||||
-- /comments CommentR POST
|
||||
|
||||
|
|
|
@ -41,3 +41,5 @@ copyright: Insert copyright statement here
|
|||
stations:
|
||||
- station-id: "test"
|
||||
station-name: "Test"
|
||||
|
||||
ad-files: "ads/"
|
||||
|
|
26
flake.lock
|
@ -5,11 +5,11 @@
|
|||
"nixpkgs-lib": "nixpkgs-lib"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1719994518,
|
||||
"narHash": "sha256-pQMhCCHyQGRzdfAkdJ4cIWiw+JNuWsTX7f0ZYSyz0VY=",
|
||||
"lastModified": 1727826117,
|
||||
"narHash": "sha256-K5ZLCyfO/Zj9mPFldf3iwS6oZStJcU4tSpiXTMYaaL0=",
|
||||
"owner": "hercules-ci",
|
||||
"repo": "flake-parts",
|
||||
"rev": "9227223f6d922fee3c7b190b2cc238a99527bbb7",
|
||||
"rev": "3d04084d54bedc3d6b8b736c70ef449225c361b1",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -20,11 +20,11 @@
|
|||
},
|
||||
"haskell-flake": {
|
||||
"locked": {
|
||||
"lastModified": 1720977934,
|
||||
"narHash": "sha256-k9kwz2lpUqafRUpuCMgkv4AWtHEoJPCds1ZPRkyW2XE=",
|
||||
"lastModified": 1728227251,
|
||||
"narHash": "sha256-JLDhMFyGyFe0QJAbCuSxB7/kjUCA7uUAMa6BFRORwXk=",
|
||||
"owner": "srid",
|
||||
"repo": "haskell-flake",
|
||||
"rev": "cd449f1c04175efdf5b553302d22916640090066",
|
||||
"rev": "9cbfbfc38f1fbf9a0f471795c84780211875fd45",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -35,11 +35,11 @@
|
|||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1721466660,
|
||||
"narHash": "sha256-pFSxgSZqZ3h+5Du0KvEL1ccDZBwu4zvOil1zzrPNb3c=",
|
||||
"lastModified": 1728279793,
|
||||
"narHash": "sha256-W3D5YpNrUVTFPVU4jiEiboaaUDShaiH5fRl9aJLqUnU=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "6e14bbce7bea6c4efd7adfa88a40dac750d80100",
|
||||
"rev": "f85a2d005e83542784a755ca8da112f4f65c4aa4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
|
@ -51,14 +51,14 @@
|
|||
},
|
||||
"nixpkgs-lib": {
|
||||
"locked": {
|
||||
"lastModified": 1719876945,
|
||||
"narHash": "sha256-Fm2rDDs86sHy0/1jxTOKB1118Q0O3Uc7EC0iXvXKpbI=",
|
||||
"lastModified": 1727825735,
|
||||
"narHash": "sha256-0xHYkMkeLVQAMa7gvkddbPqpxph+hDzdu1XdGPJR+Os=",
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz"
|
||||
},
|
||||
"original": {
|
||||
"type": "tarball",
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/5daf0514482af3f97abaefc78a6606365c9108e2.tar.gz"
|
||||
"url": "https://github.com/NixOS/nixpkgs/archive/fb192fec7cc7a4c26d51779e9bab07ce6fa5597a.tar.gz"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
|
|
|
@ -6,6 +6,9 @@
|
|||
};
|
||||
outputs = inputs@{ self, nixpkgs, flake-parts, ... }:
|
||||
flake-parts.lib.mkFlake { inherit inputs; } {
|
||||
flake = {
|
||||
nixosModules.OwOpointTracker = { import ./module.nix self
|
||||
};
|
||||
systems = nixpkgs.lib.systems.flakeExposed;
|
||||
imports = [ inputs.haskell-flake.flakeModule ];
|
||||
|
||||
|
|
89
module.nix
Normal file
|
@ -0,0 +1,89 @@
|
|||
self: {config, pkgs, lib ...}:
|
||||
with lib;
|
||||
|
||||
let
|
||||
cfg = config.services.owOPointTracker;
|
||||
format = pkgs.formats.yaml {};
|
||||
configFile = format.generate "settings.yml" cfg.config
|
||||
databaseOptionType = types.submodule {
|
||||
options = {
|
||||
database = mkOption {
|
||||
default = "_env:YESOD_SQLITE_DATABASE:OwOpointTracker.sqlite3";
|
||||
type = types.str;
|
||||
};
|
||||
poolsize = mkOption {
|
||||
default = "_env:YESOD_SQLITE_POOLSIZE:10";
|
||||
type = types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
stationType = types.submodule {
|
||||
options = {
|
||||
station-id = mkOption {
|
||||
default = "Station";
|
||||
type = types.str;
|
||||
};
|
||||
station-name = mkOption {
|
||||
types = types.str;
|
||||
};
|
||||
};
|
||||
};
|
||||
{
|
||||
imports = [];
|
||||
options.services.owOPointTracker = {
|
||||
enable = mkEnableOption "OwOPointTracker";
|
||||
config = {
|
||||
static-dir = mkOption {
|
||||
type = types.path;
|
||||
};
|
||||
host = mkOption {
|
||||
type = types.str;
|
||||
default = "_env:YESOD_HOST:*4";
|
||||
ad-files = mkOption {
|
||||
type = types.path;
|
||||
};
|
||||
port = mkOption {
|
||||
default = "_env:YESOD_PORT:3000";
|
||||
type = types.str;
|
||||
};
|
||||
ip-from-header = mkOption {
|
||||
default = "_env:YESOD_IP_FROM_HEADER:false";
|
||||
type = types.str;
|
||||
};
|
||||
database = {
|
||||
type = databaseOptionType;
|
||||
};
|
||||
copyright = mkOption {
|
||||
default = "copyright for this mess?";
|
||||
type = types.str;
|
||||
};
|
||||
station = mkOption {
|
||||
type = types.listOf stationType;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
config = mkIf cfg.enable {
|
||||
users.users.owopoint = {
|
||||
isSystemUser = true;
|
||||
group = "owopoint";
|
||||
createHome = true;
|
||||
home = "/var/owopoint";
|
||||
};
|
||||
users.groups = {
|
||||
owopoint = {};
|
||||
};
|
||||
systemd.services.owOPointTracker = {
|
||||
wantedBy = ["multi-user.target"];
|
||||
after = ["network.target"];
|
||||
serviceConfig = {
|
||||
ExecStart = "${self.packages.${pkgs.system}.default} ${configFile}";
|
||||
Restart = always;
|
||||
User = "owopoint";
|
||||
Group = "owopoint";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
}
|
|
@ -43,6 +43,7 @@ import System.Log.FastLogger (defaultBufSize, newStdoutLoggerSet,
|
|||
import Handler.Common
|
||||
import Handler.Home
|
||||
import Handler.Station
|
||||
import Handler.Ad
|
||||
|
||||
-- This line actually creates our YesodDispatch instance. It is the second half
|
||||
-- of the call to mkYesodData which occurs in Foundation.hs. Please see the
|
||||
|
|
|
@ -146,8 +146,10 @@ instance Yesod App where
|
|||
-- value passed to hamletToRepHtml cannot be a widget, this allows
|
||||
-- you to use normal widget features in default-layout.
|
||||
|
||||
-- randomAddNumber <- liftIO $ getStdRandom (randomR (0,4))
|
||||
|
||||
pc <- widgetToPageContent $ do
|
||||
addStylesheet $ StaticR css_bootstrap_css
|
||||
-- addStylesheet $ StaticR css_bootstrap_css
|
||||
-- ^ generated from @Settings/StaticFiles.hs@
|
||||
$(widgetFile "default-layout")
|
||||
withUrlRenderer $(hamletFile "templates/default-layout-wrapper.hamlet")
|
||||
|
|
15
src/Handler/Ad.hs
Normal file
|
@ -0,0 +1,15 @@
|
|||
{-# LANGUAGE NoImplicitPrelude #-}
|
||||
{-# LANGUAGE OverloadedStrings #-}
|
||||
{-# LANGUAGE TemplateHaskell #-}
|
||||
{-# LANGUAGE MultiParamTypeClasses #-}
|
||||
{-# LANGUAGE TypeFamilies #-}
|
||||
{-# LANGUAGE TypeApplications #-}
|
||||
module Handler.Ad where
|
||||
|
||||
import Import
|
||||
|
||||
getAdR :: Handler Html
|
||||
getAdR = do
|
||||
basePath <- getsYesod (appAdFilePath . appSettings)
|
||||
number <- applyAtomicGen (randomR (0 :: Int ,4)) globalStdGen
|
||||
sendFile typePng $ basePath <> "image" <> show number <> ".png"
|
|
@ -19,7 +19,8 @@ checkStation stationIdentifier = do
|
|||
Nothing -> notFound
|
||||
Just m -> return m
|
||||
|
||||
repsertBy :: (MonadIO m, PersistUniqueRead backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record, SafeToInsert record) => record -> ReaderT backend m ()
|
||||
-- TODO works without the type annotation, but I don't know why this one is wrong. No time to find out now
|
||||
-- repsertBy :: (MonadIO m, PersistUniqueRead backend, PersistRecordBackend record backend, AtLeastOneUniqueKey record, SafeToInsert record) => record -> ReaderT backend m ()
|
||||
repsertBy val = do
|
||||
valM <- getByValue val
|
||||
case valM of
|
||||
|
|
|
@ -10,3 +10,5 @@ import Settings.StaticFiles as Import
|
|||
import Yesod.Auth as Import
|
||||
import Yesod.Core.Types as Import (loggerSet)
|
||||
import Yesod.Default.Config2 as Import
|
||||
import System.Random as Import
|
||||
import System.Random.Stateful as Import
|
||||
|
|
|
@ -71,6 +71,7 @@ data AppSettings = AppSettings
|
|||
-- ^ Copyright text to appear in the footer of the page
|
||||
|
||||
, appStations :: Map Text Text
|
||||
, appAdFilePath :: String
|
||||
}
|
||||
|
||||
instance FromJSON AppSettings where
|
||||
|
@ -98,6 +99,7 @@ instance FromJSON AppSettings where
|
|||
|
||||
appCopyright <- o .: "copyright"
|
||||
appStations <- M.fromList . map (\x -> (stationId x, stationName x)) <$> o .: "stations"
|
||||
appAdFilePath <- o .: "ad-files"
|
||||
|
||||
return AppSettings {..}
|
||||
|
||||
|
|
3
static/css/test.css
Normal file
|
@ -0,0 +1,3 @@
|
|||
.blubb {
|
||||
border-width: 300px
|
||||
}
|
BIN
static/grauenhafte_werbung.gif
Normal file
After Width: | Height: | Size: 3.1 MiB |
|
@ -1,16 +1,6 @@
|
|||
<!-- Page Contents -->
|
||||
|
||||
|
||||
$if (Just HomeR == mcurrentRoute)
|
||||
<div .container>
|
||||
<div .ad>
|
||||
<img src=@{AdR}>
|
||||
^{widget}
|
||||
$else
|
||||
<div .container>
|
||||
<div .row>
|
||||
<div .col-md-12>
|
||||
^{widget}
|
||||
|
||||
<!-- Footer -->
|
||||
<footer .footer>
|
||||
<div .container>
|
||||
<p .text-muted>
|
||||
#{appCopyright $ appSettings master}
|
||||
|
|
|
@ -1,28 +1,11 @@
|
|||
.masthead,
|
||||
.navbar {
|
||||
background-color: rgb(27, 28, 29);
|
||||
body {
|
||||
font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;
|
||||
}
|
||||
|
||||
.navbar-default .navbar-nav > .active > a {
|
||||
background-color: transparent;
|
||||
border-bottom: 2px solid white;
|
||||
}
|
||||
|
||||
.navbar-nav {
|
||||
padding-bottom: 1em;
|
||||
}
|
||||
|
||||
.masthead {
|
||||
margin-top: -21px;
|
||||
color: white;
|
||||
text-align: center;
|
||||
min-height: 500px;
|
||||
}
|
||||
|
||||
.masthead .header {
|
||||
max-width: 700px;
|
||||
margin: 0 auto;
|
||||
font-family: Lato,'Helvetica Neue',Arial,Helvetica,sans-serif;
|
||||
}
|
||||
|
||||
.masthead h1.header {
|
||||
|
@ -38,36 +21,15 @@
|
|||
font-weight: normal;
|
||||
}
|
||||
|
||||
.masthead .btn {
|
||||
margin: 1em 0;
|
||||
aside,
|
||||
.ad {
|
||||
width: 25%;
|
||||
padding-left: 15px;
|
||||
margin-left: 15px;
|
||||
float: right;
|
||||
}
|
||||
|
||||
|
||||
/* Common styles for all types */
|
||||
.bs-callout {
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border: 1px solid #eee;
|
||||
border-left-width: 5px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.bs-callout p:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bs-callout-info {
|
||||
border-left-color: #1b809e;
|
||||
}
|
||||
|
||||
/* Space things out */
|
||||
.bs-docs-section {
|
||||
margin-bottom: 60px;
|
||||
}
|
||||
.bs-docs-section:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
#message {
|
||||
margin-bottom: 40px;
|
||||
img {
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
|
|
@ -1,27 +1,10 @@
|
|||
<div .masthead>
|
||||
<div .container>
|
||||
<div .row>
|
||||
<h1 .header>
|
||||
OwO Geländespiel
|
||||
OWO Geländespiel
|
||||
|
||||
<div .container>
|
||||
<!-- Starting
|
||||
================================================== -->
|
||||
<div .bs-docs-section>
|
||||
<div .row>
|
||||
<div .col-lg-12>
|
||||
<div .page-header>
|
||||
<h1 #Tutors>Tutors
|
||||
#{tutorP}
|
||||
<div .points .tutor>
|
||||
<h1 #Tutors>Tutoren: #{tutorP}
|
||||
|
||||
<hr>
|
||||
|
||||
<!-- Forms
|
||||
================================================== -->
|
||||
<div .bs-docs-section>
|
||||
<div .row>
|
||||
<div .col-lg-12>
|
||||
<div .page-header>
|
||||
<h1 #Ersties>Ersties
|
||||
#{erstieP}
|
||||
<div .points .erstie>
|
||||
<h1 #Ersties>Ersties: #{erstieP}
|
||||
|
||||
|
|
|
@ -2,3 +2,19 @@ li {
|
|||
line-height: 2em;
|
||||
font-size: 16px
|
||||
}
|
||||
.points {
|
||||
border-radius: 25px;
|
||||
font-weight: bold;
|
||||
margin: auto;
|
||||
max-width: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
margin-top: 30px;
|
||||
}
|
||||
.tutor {
|
||||
background-color: #f68afc;
|
||||
}
|
||||
.erstie {
|
||||
background-color: #00FF00;
|
||||
}
|
||||
|
|
|
@ -1,14 +1,9 @@
|
|||
<div .masthead>
|
||||
<div .container>
|
||||
<div .row>
|
||||
<h1 .header>
|
||||
OwO Geländespiel Station #{station} Round #{roundNumber}
|
||||
|
||||
<div .container>
|
||||
<div .bs-docs-section>
|
||||
<div .row>
|
||||
<div .col-lg-12>
|
||||
<div .page-header>
|
||||
<div .form>
|
||||
<form method=post action=@{StationRoundR stationIdentifier roundNumber} enctype=#{roundFormEnctype}>
|
||||
^{roundFormWidget}
|
||||
<button> Submit
|
||||
|
|
5
templates/round.lucious
Normal file
|
@ -0,0 +1,5 @@
|
|||
.form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
|
@ -1,14 +1,8 @@
|
|||
<div .masthead>
|
||||
<div .container>
|
||||
<div .row>
|
||||
<h1 .header>
|
||||
OwO Geländespiel Station #{station}
|
||||
|
||||
<div .container>
|
||||
<div .bs-docs-section>
|
||||
<div .row>
|
||||
<div .col-lg-12>
|
||||
<div .page-header>
|
||||
<div .container .form>
|
||||
<ul>
|
||||
$forall round <- rounds
|
||||
<li>
|
||||
|
|
5
templates/station.lucious
Normal file
|
@ -0,0 +1,5 @@
|
|||
.form {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|