1{ config, lib, pkgs, ... }:
2
3with lib;
4
5let
6 cfg = config.services.awstats;
7 package = pkgs.awstats;
8 configOpts = {name, config, ...}: {
9 options = {
10 type = mkOption{
11 type = types.enum [ "mail" "web" ];
12 default = "web";
13 example = "mail";
14 description = ''
15 The type of log being collected.
16 '';
17 };
18 domain = mkOption {
19 type = types.str;
20 default = name;
21 description = "The domain name to collect stats for.";
22 example = "example.com";
23 };
24
25 logFile = mkOption {
26 type = types.str;
27 example = "/var/log/nginx/access.log";
28 description = ''
29 The log file to be scanned.
30
31 For mail, set this to
32 ```
33 journalctl $OLD_CURSOR -u postfix.service | ''${pkgs.perl}/bin/perl ''${pkgs.awstats.out}/share/awstats/tools/maillogconvert.pl standard |
34 ```
35 '';
36 };
37
38 logFormat = mkOption {
39 type = types.str;
40 default = "1";
41 description = ''
42 The log format being used.
43
44 For mail, set this to
45 ```
46 %time2 %email %email_r %host %host_r %method %url %code %bytesd
47 ```
48 '';
49 };
50
51 hostAliases = mkOption {
52 type = types.listOf types.str;
53 default = [];
54 example = [ "www.example.org" ];
55 description = ''
56 List of aliases the site has.
57 '';
58 };
59
60 extraConfig = mkOption {
61 type = types.attrsOf types.str;
62 default = {};
63 example = literalExpression ''
64 {
65 "ValidHTTPCodes" = "404";
66 }
67 '';
68 description = "Extra configuration to be appended to awstats.\${name}.conf.";
69 };
70
71 webService = {
72 enable = mkEnableOption "awstats web service";
73
74 hostname = mkOption {
75 type = types.str;
76 default = config.domain;
77 description = "The hostname the web service appears under.";
78 };
79
80 urlPrefix = mkOption {
81 type = types.str;
82 default = "/awstats";
83 description = "The URL prefix under which the awstats pages appear.";
84 };
85 };
86 };
87 };
88 webServices = filterAttrs (name: value: value.webService.enable) cfg.configs;
89in
90{
91 imports = [
92 (mkRemovedOptionModule [ "services" "awstats" "service" "enable" ] "Please enable per domain with `services.awstats.configs.<name>.webService.enable`")
93 (mkRemovedOptionModule [ "services" "awstats" "service" "urlPrefix" ] "Please set per domain with `services.awstats.configs.<name>.webService.urlPrefix`")
94 (mkRenamedOptionModule [ "services" "awstats" "vardir" ] [ "services" "awstats" "dataDir" ])
95 ];
96
97 options.services.awstats = {
98 enable = mkEnableOption "awstats, a real-time logfile analyzer";
99
100 dataDir = mkOption {
101 type = types.path;
102 default = "/var/lib/awstats";
103 description = "The directory where awstats data will be stored.";
104 };
105
106 configs = mkOption {
107 type = types.attrsOf (types.submodule configOpts);
108 default = {};
109 example = literalExpression ''
110 {
111 "mysite" = {
112 domain = "example.com";
113 logFile = "/var/log/nginx/access.log";
114 };
115 }
116 '';
117 description = "Attribute set of domains to collect stats for.";
118 };
119
120 updateAt = mkOption {
121 type = types.nullOr types.str;
122 default = null;
123 example = "hourly";
124 description = ''
125 Specification of the time at which awstats will get updated.
126 (in the format described by {manpage}`systemd.time(7)`)
127 '';
128 };
129 };
130
131
132 config = mkIf cfg.enable {
133 environment.systemPackages = [ package.bin ];
134
135 environment.etc = mapAttrs' (name: opts:
136 nameValuePair "awstats/awstats.${name}.conf" {
137 source = pkgs.runCommand "awstats.${name}.conf"
138 { preferLocalBuild = true; }
139 (''
140 sed \
141 ''
142 # set up mail stats
143 + optionalString (opts.type == "mail")
144 ''
145 -e 's|^\(LogType\)=.*$|\1=M|' \
146 -e 's|^\(LevelForBrowsersDetection\)=.*$|\1=0|' \
147 -e 's|^\(LevelForOSDetection\)=.*$|\1=0|' \
148 -e 's|^\(LevelForRefererAnalyze\)=.*$|\1=0|' \
149 -e 's|^\(LevelForRobotsDetection\)=.*$|\1=0|' \
150 -e 's|^\(LevelForSearchEnginesDetection\)=.*$|\1=0|' \
151 -e 's|^\(LevelForFileTypesDetection\)=.*$|\1=0|' \
152 -e 's|^\(LevelForWormsDetection\)=.*$|\1=0|' \
153 -e 's|^\(ShowMenu\)=.*$|\1=1|' \
154 -e 's|^\(ShowSummary\)=.*$|\1=HB|' \
155 -e 's|^\(ShowMonthStats\)=.*$|\1=HB|' \
156 -e 's|^\(ShowDaysOfMonthStats\)=.*$|\1=HB|' \
157 -e 's|^\(ShowDaysOfWeekStats\)=.*$|\1=HB|' \
158 -e 's|^\(ShowHoursStats\)=.*$|\1=HB|' \
159 -e 's|^\(ShowDomainsStats\)=.*$|\1=0|' \
160 -e 's|^\(ShowHostsStats\)=.*$|\1=HB|' \
161 -e 's|^\(ShowAuthenticatedUsers\)=.*$|\1=0|' \
162 -e 's|^\(ShowRobotsStats\)=.*$|\1=0|' \
163 -e 's|^\(ShowEMailSenders\)=.*$|\1=HBML|' \
164 -e 's|^\(ShowEMailReceivers\)=.*$|\1=HBML|' \
165 -e 's|^\(ShowSessionsStats\)=.*$|\1=0|' \
166 -e 's|^\(ShowPagesStats\)=.*$|\1=0|' \
167 -e 's|^\(ShowFileTypesStats\)=.*$|\1=0|' \
168 -e 's|^\(ShowFileSizesStats\)=.*$|\1=0|' \
169 -e 's|^\(ShowBrowsersStats\)=.*$|\1=0|' \
170 -e 's|^\(ShowOSStats\)=.*$|\1=0|' \
171 -e 's|^\(ShowOriginStats\)=.*$|\1=0|' \
172 -e 's|^\(ShowKeyphrasesStats\)=.*$|\1=0|' \
173 -e 's|^\(ShowKeywordsStats\)=.*$|\1=0|' \
174 -e 's|^\(ShowMiscStats\)=.*$|\1=0|' \
175 -e 's|^\(ShowHTTPErrorsStats\)=.*$|\1=0|' \
176 -e 's|^\(ShowSMTPErrorsStats\)=.*$|\1=1|' \
177 ''
178 +
179 # common options
180 ''
181 -e 's|^\(DirData\)=.*$|\1="${cfg.dataDir}/${name}"|' \
182 -e 's|^\(DirIcons\)=.*$|\1="icons"|' \
183 -e 's|^\(CreateDirDataIfNotExists\)=.*$|\1=1|' \
184 -e 's|^\(SiteDomain\)=.*$|\1="${name}"|' \
185 -e 's|^\(LogFile\)=.*$|\1="${opts.logFile}"|' \
186 -e 's|^\(LogFormat\)=.*$|\1="${opts.logFormat}"|' \
187 ''
188 +
189 # extra config
190 concatStringsSep "\n" (mapAttrsToList (n: v: ''
191 -e 's|^\(${n}\)=.*$|\1="${v}"|' \
192 '') opts.extraConfig)
193 +
194 ''
195 < '${package.out}/wwwroot/cgi-bin/awstats.model.conf' > "$out"
196 '');
197 }) cfg.configs;
198
199 # create data directory with the correct permissions
200 systemd.tmpfiles.rules =
201 [ "d '${cfg.dataDir}' 755 root root - -" ] ++
202 mapAttrsToList (name: opts: "d '${cfg.dataDir}/${name}' 755 root root - -") cfg.configs ++
203 [ "Z '${cfg.dataDir}' 755 root root - -" ];
204
205 # nginx options
206 services.nginx.virtualHosts = mapAttrs'(name: opts: {
207 name = opts.webService.hostname;
208 value = {
209 locations = {
210 "${opts.webService.urlPrefix}/css/" = {
211 alias = "${package.out}/wwwroot/css/";
212 };
213 "${opts.webService.urlPrefix}/icons/" = {
214 alias = "${package.out}/wwwroot/icon/";
215 };
216 "${opts.webService.urlPrefix}/" = {
217 alias = "${cfg.dataDir}/${name}/";
218 extraConfig = ''
219 autoindex on;
220 '';
221 };
222 };
223 };
224 }) webServices;
225
226 # update awstats
227 systemd.services = mkIf (cfg.updateAt != null) (mapAttrs' (name: opts:
228 nameValuePair "awstats-${name}-update" {
229 description = "update awstats for ${name}";
230 script = optionalString (opts.type == "mail")
231 ''
232 if [[ -f "${cfg.dataDir}/${name}-cursor" ]]; then
233 CURSOR="$(cat "${cfg.dataDir}/${name}-cursor" | tr -d '\n')"
234 if [[ -n "$CURSOR" ]]; then
235 echo "Using cursor: $CURSOR"
236 export OLD_CURSOR="--cursor $CURSOR"
237 fi
238 fi
239 NEW_CURSOR="$(journalctl $OLD_CURSOR -u postfix.service --show-cursor | tail -n 1 | tr -d '\n' | sed -e 's#^-- cursor: \(.*\)#\1#')"
240 echo "New cursor: $NEW_CURSOR"
241 ${package.bin}/bin/awstats -update -config=${name}
242 if [ -n "$NEW_CURSOR" ]; then
243 echo -n "$NEW_CURSOR" > ${cfg.dataDir}/${name}-cursor
244 fi
245 '' + ''
246 ${package.out}/share/awstats/tools/awstats_buildstaticpages.pl \
247 -config=${name} -update -dir=${cfg.dataDir}/${name} \
248 -awstatsprog=${package.bin}/bin/awstats
249 '';
250 startAt = cfg.updateAt;
251 }) cfg.configs);
252 };
253
254}
255