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