From c5d41b09650749abf380eb8a0254e04eea48b7e9 Mon Sep 17 00:00:00 2001 From: ston1th Date: Fri, 9 Feb 2018 22:38:47 +0100 Subject: [PATCH] split into packages --- dump.test | 1 - index.go | 328 ------------------ main.go | 8 +- cmd.go => pkg/cmd/cmd.go | 11 +- pkg/db/db.go | 71 ++++ dump.go => pkg/db/dump.go | 2 +- pkg/db/helper.go | 14 + pkg/db/marshal.go | 25 ++ pkg/db/page.go | 42 +++ snippet.go => pkg/db/snippet.go | 2 +- userdb.go => pkg/db/user.go | 102 +----- pkg/index/index.go | 112 ++++++ render.go => pkg/index/render.go | 13 +- logger.go => pkg/log/log.go | 12 + cache.go => pkg/server/cache.go | 0 context.go => pkg/server/context.go | 0 editor.go => pkg/server/editor.go | 0 handler.go => pkg/server/handler.go | 0 routes.go => pkg/server/routes.go | 0 server.go => pkg/server/server.go | 0 .../server/snippet_handler.go | 0 templates.go => pkg/server/templates.go | 0 helper.go => pkg/util/helper.go | 0 templates/wikiEdit.html | 18 +- templates/wikiNew.html | 14 +- todo.txt | 2 + 26 files changed, 328 insertions(+), 449 deletions(-) delete mode 100644 dump.test delete mode 100644 index.go rename cmd.go => pkg/cmd/cmd.go (94%) create mode 100644 pkg/db/db.go rename dump.go => pkg/db/dump.go (99%) create mode 100644 pkg/db/helper.go create mode 100644 pkg/db/marshal.go create mode 100644 pkg/db/page.go rename snippet.go => pkg/db/snippet.go (99%) rename userdb.go => pkg/db/user.go (72%) create mode 100644 pkg/index/index.go rename render.go => pkg/index/render.go (90%) rename logger.go => pkg/log/log.go (69%) rename cache.go => pkg/server/cache.go (100%) rename context.go => pkg/server/context.go (100%) rename editor.go => pkg/server/editor.go (100%) rename handler.go => pkg/server/handler.go (100%) rename routes.go => pkg/server/routes.go (100%) rename server.go => pkg/server/server.go (100%) rename snippet_handler.go => pkg/server/snippet_handler.go (100%) rename templates.go => pkg/server/templates.go (100%) rename helper.go => pkg/util/helper.go (100%) create mode 100644 todo.txt diff --git a/dump.test b/dump.test deleted file mode 100644 index e52948d..0000000 --- a/dump.test +++ /dev/null @@ -1 +0,0 @@ -{"articles":[{"title":"Index","link_title":"Index","md":"# Welcome to GoWiki\nThis is the [Index](/wiki/Index) page. \nYou can customize it how you like.","created":"admin 2017-04-20 15:25:38","updated":"","public":true},{"title":"dcsdcsd","link_title":"dcsdcsd","md":"# OpenBSD httpd\r\n\r\nEnable and start service:\r\n\r\n```\r\nrcctl enable httpd\r\nrcctl start httpd\r\n```\r\n\r\n## pf Rules\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e\u003e /etc/pf.conf\r\npass in on egress proto tcp to any port http\r\npass in on egress proto tcp to any port https\r\nEOF\r\npfctl -f /etc/pf.conf\r\n```\r\n\r\n## vHosts\r\n\r\nhttpd config file: `/etc/httpd.conf`\r\n\r\n### TLS vHost\r\n\r\nCertificate using acme. More info at [OpenBSD Lets Encrypt](/wiki/OpenBSD+Lets+Encrypt)\r\n\r\n```\r\ndomain=\"example.com\"\r\n\r\nserver $domain {\r\n listen on * port 80\r\n location \"/.well-known/acme-challenge/*\" {\r\n root { \"/acme\", strip 2 }\r\n }\r\n location \"/*\" {\r\n block return 301 \"https://$SERVER_NAME$DOCUMENT_URI\"\r\n }\r\n}\r\n\r\nserver $domain {\r\n listen on * tls port 443\r\n tls {\r\n certificate \"/etc/ssl/acme/example.com/fullchain.pem\"\r\n key \"/etc/ssl/acme/private/example.com/privkey.pem\"\r\n ecdhe secp384r1\r\n ciphers ECDHE-RSA-CHACHA20-POLY1305:CHACHA20:ECDHE+AESGCM:ECDHE+AES\r\n }\r\n hsts { max-age 31536000, subdomains }\r\n location \"/\" {\r\n directory auto index\r\n }\r\n}\r\n```\r\n## robots.txt\r\n\r\nCreate a new directory: `mkdir /var/www/robots`\r\n\r\nCreate the robots.txt file:\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e /var/www/robots/robots.txt\r\nUser-agent: *\r\nDisallow: /\r\nEOF\r\n```\r\n\r\nAdd this to your httpd.conf:\r\n\r\n```\r\nlocation \"/robots.txt\" {\r\n root \"/robots\"\r\n}\r\n```","created":"admin 2017-04-22 13:14:30","updated":"admin 2017-04-22 13:23:49","public":true},{"title":"mail","link_title":"mail","md":"# OpenBSD Mail Server\r\n\r\n## Intro\r\n\r\nThis is how to setup an OpenBSD Mail Server with:\r\n\r\n* OpenSMTPD (mta)\r\n* Spamd\r\n* ClamAV (in and out scanning)\r\n* Zarafa (webapp, z-push) not included are IMAP, POP3, CalDav. Maybe they will be added later.\r\n\r\n\r\nNote: This is mostly a copy/past guide. It is still recommended to read the man-pages to 'understand' why and how things happen.\r\n\r\n## Mail-Flow\r\n\r\n### Incoming mail\r\n\r\n```\r\npf -\u003e relay to spamd -\u003e send to opensmtpd on lo0 -\u003e relay untagged mail to clamsmtpd on port 10025 -\u003e relay to clamd on port 3310 -\u003e\r\nreturn to clamsmtpd -\u003e return to opensmtpd on lo0 port 10026 and tag it CLAM_IN -\u003e deliver to mda (zarafa-dagent)\r\n```\r\n\r\n### Outgoing mail\r\n\r\n```\r\nzarafa (zarafa-spooler) -\u003e opensmtpd on lo0 -\u003e relay untagged mail to clamsmtpd on port 10027 -\u003e relay to clamd on port 3310 -\u003e\r\nreturn to clamsmtpd -\u003e return to opensmtpd on lo0 port 10028 and tag it CLAM_OUT -\u003e relay out\r\n```\r\n\r\n## smtpd\r\n\r\nBoot startup: \r\n\r\n```\r\nrcctl enable smtpd\r\nrcctl set smtpd flags -v\r\n```\r\n\r\n### Virtual Domains\r\n\r\nThis file holds all virtual domains:\r\n\r\n```\r\ncat /etc/mail/vdomains\r\nexample.com\r\nexample.org\r\n```\r\n\r\n### Virtual Users\r\n\r\nThis file holds all virtual users:\r\n\r\n```\r\ncat /etc/mail/vusers\r\njohn@example.com john\r\n@example.com john@example.com\r\ndoe@example.org doe\r\nhostmaster@example.org doe@example.org\r\n```\r\n\r\nThis file holds all user mappings:\r\n\r\n```\r\ncat /etc/mail/umap\r\njohn 1000:100:/var/virtual\r\ndoe 1000:100:/var/virtual\r\n```\r\n\r\n### smtpd.conf\r\n\r\nReplace `example.com` with your domain.\r\n\r\n```\r\nmax-message-size 20MB\r\npki mail.example.com certificate \"/etc/ssl/acme/example.com/fullchain.pem\"\r\npki mail.example.com key \"/etc/ssl/acme/private/example.com/privkey.pem\"\r\nciphers ECDHE-RSA-CHACHA20-POLY1305:CHACHA20:ECDHE+AESGCM:ECDHE+AES\r\n\r\nlisten on lo0\r\nlisten on lo0 port 10026 tag CLAM_IN \r\nlisten on lo0 port 10028 tag CLAM_OUT \r\nlisten on egress tls pki mail.example.com auth-optional\r\n\r\ntable aliases file:/etc/mail/aliases\r\ntable vdomains file:/etc/mail/vdomains\r\ntable vusers file:/etc/mail/vusers\r\ntable umap file:/etc/mail/umap\r\n\r\naccept for local alias \u003caliases\u003e deliver to mbox\r\n\r\naccept tagged CLAM_IN for domain \u003cvdomains\u003e virtual \u003cvusers\u003e userbase \u003cumap\u003e \\\r\n deliver to mda \"/usr/local/sbin/zarafa-dagent %{dest}\" as \"_zarafa\"\r\naccept tagged CLAM_OUT for any relay\r\n\r\naccept from any for domain \u003cvdomains\u003e relay via smtp://127.0.0.1:10025 \r\naccept from local for any relay via smtp://127.0.0.1:10027\r\n```\r\n\r\nRestart smtpd: `rcctl restart smtpd`\r\n\r\n## spamd\r\n\r\nCreate custom spam files:\r\n\r\n```\r\ntouch /etc/mail/nospamd\r\ntouch /etc/mail/spamd.black\r\n```\r\n\r\nConfig:\r\n\r\n```\r\nall:\\\r\n :nixspam:custom_black:\r\n\r\nnixspam:\\\r\n :black:\\\r\n :msg=\"Your address %A is in the nixspam list\\n\\\r\n See http://www.heise.de/ix/nixspam/dnsbl_en/ for details\":\\\r\n :method=http:\\\r\n :file=www.openbsd.org/spamd/nixspam.gz\r\n\r\ncustom_black:\\\r\n :black:\\\r\n :msg=\"Your address %A has been manually blacklisted\":\\\r\n :method=file:\\\r\n :file=/etc/mail/custom.black:\r\n```\r\n\r\nBoot startup: \r\n\r\nReplace `example.com` with your domain.\r\n\r\n```\r\nrcctl enable spamd\r\nrcctl set spamd flags \"-v -G 10:4:864 -K /etc/ssl/acme/private/example.com/privkey.pem -C /etc/ssl/acme/example.com/fullchain.pem\"\r\nrcctl enable spamlogd\r\n```\r\n\r\nSince we use 10 minutes greylisting `-G 10:` reload the blacklist every 5 minutes.\r\n\r\nCreate `spamd-setup` cronjob: `echo \"5 * * * * /usr/libexec/spamd-setup -b \u003e/dev/null 2\u003e\u00261\" \u003e\u003e /var/cron/tabs/root`\r\n\r\n### pf Rules\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e\u003e /etc/pf.conf\r\nset limit table-entries 400000\r\n\r\ntable \u003cspamd\u003e persist\r\ntable \u003cnospamd\u003e persist file \"/etc/mail/nospamd\"\r\ntable \u003cspamd-white\u003e persist\r\n\r\npass in quick log on egress proto tcp \\\r\n from { \u003cnospamd\u003e, \u003cspamd-white\u003e } \\\r\n to any port smtp \\\r\n flags S/SA modulate state\r\n\r\npass in quick proto tcp from \u003cspamd\u003e \\\r\n to egress port { smtp, submission, smtps } \\\r\n rdr-to 127.0.0.1 port spamd\r\n\r\npass in quick proto tcp from any \\\r\n to egress port smtp \\\r\n rdr-to 127.0.0.1 port spamd\r\n\r\npass out log on egress proto tcp to any port smtp\r\nEOF\r\npfctl -f /etc/pf.conf\r\n```\r\n\r\nStart spamd and spamlogd:\r\n\r\n```\r\nrcctl start spamd\r\nrcctl start spamlogd\r\n```\r\n\r\n## ClamAV\r\n\r\nInstall: `pkg_add clamav clamsmtp`\r\n\r\nBoot startup:\r\n\r\n```\r\nrcctl enable clamd\r\nrcctl enable clamsmtpd_in\r\nrcctl enable clamsmtpd_out\r\n```\r\n\r\n### Config\r\n\r\n#### clamd\r\n\r\n```\r\n/etc/clamd.conf\r\n\r\nComment out the 'Example' string:\r\n\r\n#Example\r\n\r\nUncomment these options:\r\n\r\nTCPSocket 3310\r\nTCPAddr 127.0.0.1\r\n```\r\n\r\n#### freshclam\r\n\r\n```\r\n/etc/freshclam.conf\r\n\r\nComment out the 'Example' string:\r\n\r\n#Example\r\n\r\nChange\r\n#DatabaseMirror db.XY.clamav.net\r\nto\r\nDatabaseMirror db.de.clamav.net\r\n```\r\n\r\nCreate the update cronjob:\r\n\r\n`echo \"20 * * * * /usr/local/bin/freshclam \u003e/dev/null 2\u003e\u00261\" \u003e\u003e /var/cron/tabs/root`\r\n\r\n#### clamsmtpd\r\n\r\nCreate two clamsmtpd configs:\r\n\r\n```\r\ncp /etc/clamsmtpd.conf /etc/clamsmtpd-in.conf\r\ncp /etc/clamsmtpd.conf /etc/clamsmtpd-out.conf\r\n```\r\n\r\n```\r\n/etc/clamsmtpd-in.conf\r\nChange to match these values:\r\nOutAddress: 10026\r\nListen: 127.0.0.1:10025\r\nUser: _clamav\r\n\r\nAdd this option:\r\nClamAddress: 127.0.0.1:3310\r\n```\r\n\r\n```\r\n/etc/clamsmtpd-out.conf\r\nChange to match these values:\r\nOutAddress: 10028\r\nListen: 127.0.0.1:10027\r\nUser: _clamav\r\n\r\nAdd this option:\r\nClamAddress: 127.0.0.1:3310\r\n```\r\n\r\nCreate the two init scripts:\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e /etc/rc.d/clamsmtpd_in\r\n#!/bin/sh\r\n\r\ndaemon=\"/usr/local/sbin/clamsmtpd -f /etc/clamsmtpd-in.conf\"\r\n\r\n. /etc/rc.d/rc.subr\r\n\r\nrc_cmd $1\r\nEOF\r\n```\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e /etc/rc.d/clamsmtpd_out\r\n#!/bin/sh\r\n\r\ndaemon=\"/usr/local/sbin/clamsmtpd -f /etc/clamsmtpd-out.conf\"\r\n\r\n. /etc/rc.d/rc.subr\r\n\r\nrc_cmd $1\r\nEOF\r\n```\r\n\r\n`chmod 0755 /etc/rc.d/clamsmtpd_{in,out}`\r\n\r\nRun the initial freshclam: `/usr/local/bin/freshclam`\r\n\r\nStart the daemons:\r\n\r\n```\r\nrcctl start clamd\r\nrcctl start clamsmtpd_in\r\nrcctl start clamsmtpd_out\r\n```\r\n\r\n## MariaDB\r\n\r\nInstall: `pkg_add mariadb-server`\r\n\r\nBoot startup: `rcctl enable mysqld`\r\n\r\n### Init mysql\r\n\r\n```\r\nmysql_install_db\r\nrcctl start mysqld\r\nmysql_secure_installation\r\n```\r\n\r\n### Config\r\n\r\n```\r\n/etc/my.cnf\r\n\r\nComment out these options:\r\n[client]\r\nport = 3306\r\n\r\n[mysqld]\r\nport = 3306\r\nbind-address = 127.0.0.1\r\n```\r\n\r\nRestart mysqld: `rcctl restart mysqld`\r\n\r\n### Database\r\n\r\n```\r\nmysql -u root -p\r\n\r\nCREATE DATABASE zarafa;\r\nGRANT ALL ON zarafa.* TO 'zarafa'@'localhost' IDENTIFIED BY '\u003cpassword\u003e';\r\nFLUSH PRIVILEGES;\r\n```\r\n\r\n## Zarafa\r\n\r\nInstall: `pkg_add zarafa`\r\n\r\nBoot startup: `rcctl enable zarafa`\r\n\r\n### Config\r\n\r\n```\r\n/etc/rc.d/zarafa\r\n\r\nChange\r\n_pkg_scripts=\"zarafa_server zarafa_spooler zarafa_monitor zarafa_gateway zarafa_ical\"\r\nto\r\n_pkg_scripts=\"zarafa_server zarafa_spooler zarafa_monitor\"\r\n```\r\n\r\nAdd this after the `rc_pre()` function in `/etc/rc.d/zarafa_server`:\r\n\r\n```\r\nsock=\"/var/run/zarafad/server.sock\"\r\nnew=\"/var/www/run/server.sock\"\r\n\r\nrc_start() {\r\n ${rcexec} \"${daemon} ${daemon_flags} ${_bg}\"\r\n exc=$?\r\n sleep 2\r\n ln $sock $new\r\n chgrp www $new\r\n return $exc\r\n}\r\n\r\nrc_post() {\r\n rm -f $new\r\n}\r\n```\r\n\r\n```\r\n/etc/zarafa/server.cfg\r\n\r\nChange\r\nserver_tcp_enabled = yes\r\nto\r\nserver_tcp_enabled = no\r\n\r\nSet mysql credentials\r\nmysql_user = zarafa\r\nmysql_password = \u003cpassword\u003e\r\nmysql_socket = /var/run/mysql/mysql.sock\r\n```\r\n\r\nAdd _zarafa user to the user mapping:\r\n\r\n```\r\nawk -F':' '/_zarafa/{print $1\" \"$3\":\"$4\":\"$6}' /etc/passwd \u003e\u003e /etc/mail/umap\r\n```\r\n\r\nRestart smtpd: `rcctl restart smtpd`\r\n\r\nStart zarafa: `rcctl start zarafa`\r\n\r\nCreate a user: `zarafa-admin -c \"john@example.com\" -f \"John Doe\" -e \"john@example.com\" -P`\r\n\r\n## Zarafa Webapp\r\n\r\nInstall: `pkg_add zarafa-webapp`\r\n\r\nBoot startup: `rcctl enable php56_fpm`\r\n\r\n### Config\r\n\r\n`echo \"extension=mapi.so\" \u003e /etc/php-5.6/zarafa.ini`\r\n\r\n```\r\n/etc/php-5.6.ini\r\n\r\nChange\r\nexpose_php = On\r\nto\r\nexpose_php = off\r\n```\r\n\r\n```\r\n/var/www/zarafa-webapp/config.php\r\n\r\nComment out this if/else block and insert the single DEFAULT_SERVER define.\r\nIt should look like this:\r\n\r\n /*if(version_compare(phpversion('mapi'), '7.2.2', '\u003e=')) {\r\n define(\"DEFAULT_SERVER\", \"default:\");\r\n } else {\r\n #define(\"DEFAULT_SERVER\",\"http://localhost:236/zarafa\");\r\n define(\"DEFAULT_SERVER\",\"file:///var/run/zarafa\");\r\n }*/\r\n define(\"DEFAULT_SERVER\", \"file:///run/server.sock\");\r\n```\r\n\r\nStart php-fpm: `rcctl start php56_fpm`\r\n\r\n## Z-Push\r\n\r\nInstall: `pkg_add z-push`\r\n\r\n### Config\r\n\r\nCreate directories:\r\n\r\n```\r\nmkdir -p /var/www/var/{db/z-push,log/z-push}\r\nchown www /var/www/var/{db/z-push,log/z-push}\r\n```\r\n\r\n```\r\n/var/www/z-push/config.php\r\n\r\nChange if needed\r\ndefine('USE_FULLEMAIL_FOR_LOGIN', false);\r\nto\r\ndefine('USE_FULLEMAIL_FOR_LOGIN', true);\r\n```\r\n\r\n```\r\n/var/www/z-push/backend/zarafa/config.php\r\n\r\nChange\r\ndefine('MAPI_SERVER', 'http://127.0.0.1:236/zarafa');\r\nto\r\ndefine('MAPI_SERVER', 'file:///run/server.sock');\r\n```\r\n\r\n## httpd\r\n\r\nBasic config at [OpenBSD httpd](/wiki/OpenBSD+httpd)\r\n\r\nZarafa config:\r\n\r\n```\r\n root \"/zarafa-webapp\"\r\n directory index \"index.php\"\r\n location \"*.php\" {\r\n fastcgi socket \"/run/php-fpm.sock\"\r\n }\r\n location \"/Microsoft-Server-ActiveSync\" {\r\n root \"/z-push/index.php\"\r\n fastcgi socket \"/run/php-fpm.sock\"\r\n }\r\n```\r\n\r\n# Optionals\r\n\r\n## OpenBGPD\r\n\r\nProject: [bgp-spamd](http://bgp-spamd.net/)\r\n\r\nBoot startup: `rcctl enable bgpd`\r\n\r\n### Config\r\n\r\nOpenBGPD config:\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e /etc/bgpd.conf\r\nspamdAS=\"65066\"\r\n\r\nAS 65001\r\n# Mandatory, to not update the local routing table\r\nfib-update no\r\n\r\ngroup \"spamd-bgp\" {\r\n remote-as $spamdAS\r\n multihop 64\r\n\r\n # Do not send Route Server any information\r\n announce none\r\n\r\n # us.bgp-spamd.net\r\n neighbor 64.142.121.62\r\n\r\n # eu.bgp-spamd.net\r\n neighbor 217.31.80.170\r\n\r\n # IPv6 eu.bgp-spamd.net\r\n #neighbor 2a00:15a8:0:100:0:d91f:50aa:1\r\n}\r\n \r\n# 'match' is required, to remove entries when routes are withdrawn\r\nmatch from group \"spamd-bgp\" community $spamdAS:42 set pftable \"bgp_spamd_bypass\"\r\nmatch from group \"spamd-bgp\" community $spamdAS:666 set pftable \"bgp_spamd\"\r\nEOF\r\nchmod 0600 /etc/bgpd.conf\r\n```\r\n\r\nStart OpenBGPD: `rctl start pbgd`\r\n\r\nAdd these options to your `/etc/pf.conf`:\r\n\r\n```\r\nset limit table-entries 400000\r\n\r\ntable \u003cbgp_spamd_bypass\u003e persist\r\ntable \u003cbgp_spamd\u003e persist\r\n\r\nbgp_addrs = \"{ 64.142.121.62, 217.31.80.170, 2a00:15a8:0:100:0:d91f:50aa:1 }\"\r\npass in on egress proto tcp from $bgp_addrs to any port bgp\r\n```\r\n\r\nChange your smtp rules to match these:\r\n\r\n```\r\npass in quick log on egress proto tcp \\\r\n from { \u003cbgp_spamd_bypass\u003e, \u003cnospamd\u003e, \u003cspamd-white\u003e } \\\r\n to any port smtp \\\r\n flags S/SA modulate state\r\n\r\npass in quick proto tcp from { \u003cbgp_spamd\u003e, \u003cspamd\u003e } \\\r\n to egress port { smtp, submission, smtps } \\\r\n rdr-to 127.0.0.1 port spamd\r\n```\r\n\r\nReload pf: `pfctl -f /etc/pf.conf`\r\n\r\nEdit the `/etc/mail/spamd.conf`:\r\n\r\n```\r\n# Add bgp-spamd to the all list:\r\nall:\\\r\n :nixspam:custom_black:bgp-spamd:\r\n\r\n# Add the bgp-spamd list:\r\nbgp-spamd:\\\r\n :black:\\\r\n :msg=\"Your address %A has sent mail to a spamtrap\\n\\\r\n within the last 24 hours\":\\\r\n :method=file:\\\r\n :file=/etc/mail/spamd.black:\r\n```\r\n\r\nCreate the spamd-reload script:\r\n\r\n```\r\ncat \u003c\u003cEOF\u003e /usr/local/sbin/bgp-spamd.black.sh\r\n#!/bin/sh\r\n\r\nblack=\"/etc/mail/spamd.black\"\r\n\r\nbgpctl show rib community 65066:666 | tail -n +5 |\r\n awk '{sub(/\\/.*/, \"\", $1);print $1}' |\r\n uniq \u003e ${black}\r\n#sed -e '1,4d' -e 's/\\/.*$//' -e 's/[ \\*\\\u003e]*//' \u003e ${black}\r\n\r\n/usr/libexec/spamd-setup -b\r\nEOF\r\nchmod 0744 /usr/local/sbin/bgp-spamd.black.sh\r\n```\r\n\r\nReplace the `spamd-setup` cronjob from above with the new script and run it once manually: `/usr/local/sbin/bgp-spamd.black.sh`\r\n\r\n## SpamAssassin\r\n\r\nInstall: `pkg_add p5-Mail-SpamAssassin spampd`\r\n\r\nBoot startup:\r\n\r\n```\r\nrcctl enable spampd\r\nrcctl enable spamassassin\r\nrcctl set spampd flags \"--port=10029 --relayhost=127.0.0.1:10030 --tagall --aw\"\r\n```\r\n\r\n### Config\r\n\r\n```\r\n/etc/mail/spamassassin/local.cf\r\n\r\nUncomment this option\r\nrewrite_header\r\n```\r\n\r\nChange your `smtpd.conf` to match this:\r\n\r\n```\r\n/etc/mail/smtpd.conf\r\n\r\nlisten on lo0 port 10030 tag SPAM_IN\r\n\r\naccept tagged SPAM_IN for domain \u003cvdomains\u003e virtual \u003cvusers\u003e userbase \u003cumap\u003e \\\r\n deliver to mda \"/usr/local/sbin/zarafa-dagent %{dest}\" as \"_zarafa\"\r\naccept tagged CLAM_IN for any relay via smtp://127.0.0.1:10029\r\naccept tagged CLAM_OUT for any relay\r\n```\r\n\r\nStart the daemons:\r\n\r\n```\r\nrcctl start spampd\r\nrcctl start spamassassin\r\n```\r\n\r\nRestart OpenSMTPD: `rcctl restart smtpd`\r\n\r\n## Backup\r\n\r\n```\r\n#!/bin/sh\r\n\r\ndb=\"zarafa\"\r\ndb_user=\"zarafa\"\r\ndb_pw=\"\u003cpassword\u003e\"\r\n\r\nset -A paths \\\r\n/var/db/zarafa \\\r\n/etc/zarafa \\\r\n/etc/mail \\\r\n/etc/pf.conf \\\r\n/etc/httpd.conf \\\r\n/etc/clamsmtpd-in.conf \\\r\n/etc/clamsmtpd-out.conf \\\r\n/etc/clamd.conf \\\r\n/etc/freshclam.conf \\\r\n/etc/ssl/acme \\\r\n/etc/acme \\\r\n/etc/bgpd.conf \\\r\n/etc/rc.conf.local \\\r\n/etc/rc.d/clamsmtpd_in \\\r\n/etc/rc.d/clamsmtpd_out \\\r\n/etc/my.cnf \\\r\n/etc/rc.d/zarafa \\\r\n/etc/rc.d/zarafa_server \\\r\n/etc/php-5.6/zarafa.ini \\\r\n/etc/php-5.6.ini \\\r\n/var/www/zarafa-webapp/config.php \\\r\n/var/www/z-push/config.php \\\r\n/var/www/z-push/backend/zarafa/config.php \\\r\n/usr/local/sbin/bgp-spamd.black.sh \\\r\n/usr/local/sbin/cron-acme-update \\\r\n/etc/mail/spamassassin/local.cf \\\r\n/var/cron/tabs/root \\\r\n/tmp/${db}.sql\r\n\r\nmysqldump -u ${db_user} -p${db_pw} ${db} \u003e/tmp/${db}.sql\r\n\r\ntar cfvz /root/mailserver_backup_$(date +%Y%m%d_%H%M%S).tgz ${paths[@]}\r\n\r\nrm /tmp/${db}.sql\r\n```\r\n\r\n# END","created":"admin 2017-04-22 13:55:37","updated":"","public":true},{"title":"sdcsdcs","link_title":"sdcsdcs","md":"# Bla\r\n\r\n## HTTP\r\n\r\n### Config\r\n\r\nBla\r\n\r\n## HTTPS\r\n\r\n### Config\r\n\r\nBlubb","created":"admin 2017-04-22 13:45:27","updated":"admin 2017-04-22 13:54:42","public":true}],"snippets":null,"users":[{"Username":"admin","Password":"$2a$13$M5UIpGUG4WmEItvd7c6Vi.x1aOQ4U3wz1SOScrXT0C3dUy8eNiu7m","Admin":true,"Locked":0}]} diff --git a/index.go b/index.go deleted file mode 100644 index 5eda052..0000000 --- a/index.go +++ /dev/null @@ -1,328 +0,0 @@ -// Copyright (C) 2017 Marius Schellenberger - -package main - -import ( - "fmt" - "html/template" - "os" - - "github.com/blevesearch/bleve" - "github.com/blevesearch/bleve/document" -) - -const ( - maxResult = 1e6 - maxSearchResult = 101 - noResults = "No search results" - indexName = "Index" - welcome = `# Welcome to GoWiki -This is the [Index](/wiki/Index) page. -You can customize it how you like.` -) - -type Article struct { - Title string `json:"title"` - LinkTitle string `json:"-"` - Search string `json:"search"` - Index template.HTML `json:"index"` - Text template.HTML `json:"text"` - MD string `json:"md"` - Created string `json:"created"` - Updated string `json:"updated"` - Public bool `json:"public"` -} - -func notExists(title string) error { - return indexErr("article '" + title + "' does not esist") -} - -func indexErr(i interface{}) error { - return fmt.Errorf("index: %s", i) -} - -type Index struct { - index bleve.Index - cache *IndexCache -} - -func NewIndex(path string) (i *Index, err error) { - var bi bleve.Index - if _, err = os.Stat(path); os.IsNotExist(err) { - mapping := bleve.NewIndexMapping() - bi, err = bleve.New(path, mapping) - if err != nil { - err = indexErr(err) - return - } - i = &Index{index: bi} - _, err = i.Index(indexName, welcome, defUser, true) - if err != nil { - err = indexErr(err) - return - } - } else { - bi, err = bleve.Open(path) - if err != nil { - err = indexErr(err) - return - } - i = &Index{index: bi} - } - i.cache = NewIndexCache() - return -} - -func NewDumpIndex(path string) (i *Index, err error) { - bi, err := bleve.OpenUsing(path, map[string]interface{}{"read_only": true}) - if err != nil { - err = indexErr(err) - return - } - i = &Index{index: bi} - return -} - -func (i *Index) Close() error { - return i.index.Close() -} - -func (i *Index) Restore(dumpArt *DumpArticle) error { - art := render(dumpArt.MD).Article() - art.Title = dumpArt.Title - art.Created = dumpArt.Created - art.Updated = dumpArt.Updated - art.Public = dumpArt.Public - return i.index.Index(dumpArt.LinkTitle, art) -} - -func (i *Index) Index(title, text, user string, public bool) (string, error) { - if user == "" { - return "", indexErr("user cannot be empty") - } - newTitle, title := makeLinkTitle(title) - if i.exists(false, newTitle) { - return "", indexErr("article '" + title + "' already exists") - } - art := render(text).Article() - art.Title = title - art.Created = user + " " + now() - art.Public = public - err := i.index.Index(newTitle, art) - i.cache.Add(newTitle, art) - return newTitle, err -} - -func (i *Index) exists(public bool, title string) bool { - _, err := i.Get(public, title) - return err == nil -} - -func (i *Index) Delete(title string) error { - if title == indexName { - return indexErr("page '" + indexName + "' cannot be deleted") - } - if !i.exists(false, title) { - return notExists(title) - } - err := i.index.Delete(title) - if err != nil { - return err - } - i.cache.Delete(title) - return err -} - -func (i *Index) Update(title, text, user string, public bool) error { - if user == "" { - return indexErr("user cannot be empty") - } - if title == indexName && !public { - return indexErr("page '" + indexName + "' must stay public") - } - if !i.exists(false, title) { - return notExists(title) - } - art, err := i.Get(false, title) - if err != nil { - return err - } - rend := render(text).Article() - art.Updated = user + " " + now() - art.Search = rend.Search - art.Index = rend.Index - art.Text = rend.Text - art.MD = rend.MD - art.Public = public - err = i.index.Index(title, art) - if err != nil { - return err - } - i.cache.Add(title, &art) - return err -} - -func (i *Index) Get(public bool, title string) (art Article, err error) { - a := i.cache.Get(title) - switch { - case a != nil: - art = *a - default: - var doc *document.Document - doc, err = i.index.Document(title) - if err != nil { - return - } - if doc == nil || doc.Fields == nil { - err = notExists(title) - return - } - for _, field := range doc.Fields { - switch f := field.(type) { - case *document.TextField: - switch field.Name() { - case "title": - art.Title = string(f.Value()) - case "index": - art.Index = template.HTML(string(f.Value())) - case "text": - art.Text = template.HTML(string(f.Value())) - case "md": - art.MD = string(f.Value()) - case "created": - art.Created = string(f.Value()) - case "updated": - art.Updated = string(f.Value()) - } - case *document.BooleanField: - if field.Name() == "public" { - p, e := f.Boolean() - if e != nil { - err = e - return - } - art.Public = p - } - } - } - } - art.LinkTitle = title - if public && !art.Public { - err = notExists(title) - } - return -} - -func (i *Index) Search(public bool, search string) (html string, err error) { - if search == "" { - html = noResults - return - } - var ( - res *bleve.SearchResult - req *bleve.SearchRequest - ) - if public { - req = bleve.NewSearchRequest(bleve.NewConjunctionQuery([]bleve.Query{ - bleve.NewBoolFieldQuery(true).SetField("public"), - bleve.NewQueryStringQuery("title:" + search + " search:" + search), - })) - } else { - req = bleve.NewSearchRequest(bleve.NewQueryStringQuery("title:" + search + " search:" + search)) - } - req.Highlight = bleve.NewHighlightWithStyle("html") - req.Size = maxSearchResult - res, err = i.index.Search(req) - if err != nil { - return - } - if res.Total == 0 { - html = noResults - return - } else if res.Total == maxSearchResult { - err = indexErr("too many search results") - return - } - for _, hit := range res.Hits { - if hit == nil { - continue - } - art, err := i.Get(false, hit.ID) - if err != nil { - continue - } - html += `` + art.Title + "" - for fragField, frags := range hit.Fragments { - if fragField == "title" || fragField == "search" { - html += `
`
-				for _, f := range frags {
-					html += f
-				}
-				html += "
" - break - } - } - html += "
" - } - return -} - -type Title struct { - Title string - LinkTitle string -} - -func (i *Index) GetAll(public bool) (t []Title, err error) { - var ( - res *bleve.SearchResult - req *bleve.SearchRequest - ) - if public { - req = bleve.NewSearchRequest(bleve.NewBoolFieldQuery(true).SetField("public")) - } else { - req = bleve.NewSearchRequest(bleve.NewMatchAllQuery()) - } - req.Size = maxResult - res, err = i.index.Search(req) - if err != nil { - return - } - for _, v := range res.Hits { - if v == nil { - continue - } - if v.ID == indexName { - continue - } - doc, err := i.index.Document(v.ID) - if err != nil || doc == nil || doc.Fields == nil { - continue - } - for _, field := range doc.Fields { - if field.Name() == "title" { - f, ok := field.(*document.TextField) - if ok { - t = append(t, Title{string(f.Value()), v.ID}) - break - } - } - } - } - return -} - -func (i *Index) Dump() (titles []string, err error) { - req := bleve.NewSearchRequest(bleve.NewMatchAllQuery()) - req.Size = maxResult - res, err := i.index.Search(req) - if err != nil { - return - } - for _, v := range res.Hits { - if v == nil { - continue - } - titles = append(titles, v.ID) - } - return -} diff --git a/main.go b/main.go index 3f7c614..df478d7 100644 --- a/main.go +++ b/main.go @@ -4,14 +4,10 @@ package main -import "github.com/urfave/cli" +import "git.giftfish.de/ston1th/gowiki/pkg/cmd" var version string func main() { - app := cli.NewApp() - app.Name = "gowiki" - app.Usage = "a simple wiki engine" - app.Version = version - run(app) + cmd.Run(version) } diff --git a/cmd.go b/pkg/cmd/cmd.go similarity index 94% rename from cmd.go rename to pkg/cmd/cmd.go index f50f4d5..41d3320 100644 --- a/cmd.go +++ b/pkg/cmd/cmd.go @@ -12,6 +12,8 @@ import ( "os/signal" "syscall" "time" + + "git.giftfish.de/ston1th/gowiki/pkg/log" ) const ( @@ -41,7 +43,7 @@ func server(conf serverConfig) (err error) { return } - newLogger(conf.LogFile) + log.NewLogger(conf.LogFile) l, err := godrop.GetListener() if err != nil { @@ -60,6 +62,7 @@ func server(conf serverConfig) (err error) { log.Println("caught " + sig.String()) go func() { time.Sleep(time.Second * 20) + //TODO error needed? log.Fatal(errors.New("stop timed out: killing")) }() err = srv.Stop() @@ -75,7 +78,11 @@ var ( conf serverConfig ) -func run(app *cli.App) { +func Run(version string) { + app := cli.NewApp() + app.Name = "gowiki" + app.Usage = "a simple wiki engine" + app.Version = version app.Commands = []cli.Command{ { Name: "server", diff --git a/pkg/db/db.go b/pkg/db/db.go new file mode 100644 index 0000000..9d3524b --- /dev/null +++ b/pkg/db/db.go @@ -0,0 +1,71 @@ +// Copyright (C) 2017 Marius Schellenberger + +package db + +import ( + "bytes" + "errors" + "github.com/boltdb/bolt" +) + +const ( + dbFile = "gowiki.db" + defUser = "admin" + defPassword = "gowiki" + userTable = "user" + pageTable = "page" + bcryptCost = 13 + indexName = "Index" + welcome = `# Welcome to GoWiki +This is the [Index](/wiki/Index) page. +You can customize it how you like.` +) +const ( + maxResult = 1e6 + maxSearchResult = 101 +) + +var ( + errUserNotExists = dbErr("user does not exist") + errUserExists = dbErr("user already exist") +) + +func dbErr(i interface{}) error { + return fmt.Errorf("db: %s", i) +} + +type BoltStore struct { + Marshaler + db *bolt.DB +} + +func NewBoltStore() (bs *BoltStore, err error) { + db, err := bolt.Open(dbFile, 0666, nil) + if err != nil { + return + } + if err = db.Update(func(tx *bolt.Tx) error { + if _, err := tx.CreateBucketIfNotExists([]byte(userTable)); err != nil { + return err + } + if _, err := tx.CreateBucketIfNotExists([]byte(pagesTable)); err != nil { + return err + } + return nil + }); err != nil { + return + } + bs = &BoltStore{NewGOB(), db} + if !bs.UserExists(defUser) { + err = bs.CreateUser(defUser, defPassword, true) + if err != nil { + return + } + } + bs.UnlockUser(defUser) + return +} + +func (bs *BoltStore) Close() error { + return bs.db.Close() +} diff --git a/dump.go b/pkg/db/dump.go similarity index 99% rename from dump.go rename to pkg/db/dump.go index 5712078..213681e 100644 --- a/dump.go +++ b/pkg/db/dump.go @@ -1,6 +1,6 @@ // Copyright (C) 2017 Marius Schellenberger -package main +package db import ( "encoding/json" diff --git a/pkg/db/helper.go b/pkg/db/helper.go new file mode 100644 index 0000000..797d0ca --- /dev/null +++ b/pkg/db/helper.go @@ -0,0 +1,14 @@ +package db + +import "golang.org/x/crypto/sha3" + +func validatePassword(pw string) (b []byte) { + b = []byte(pw) + if len(pw) <= 56 { + return + } + hash := sha3.New384() + hash.Write(b) + b = hash.Sum(nil) + return +} diff --git a/pkg/db/marshal.go b/pkg/db/marshal.go new file mode 100644 index 0000000..08cbf28 --- /dev/null +++ b/pkg/db/marshal.go @@ -0,0 +1,25 @@ +package db + +import "encoding/gob" + +type Marshaler interface { + Marshal(v interface{}) ([]byte, error) + Unmarshal(data []byte, v interface{}) error +} + +type gobMarshaler struct{} + +func NewGOB() Marshaler { + return gobMarshaler{} +} + +func (gobMarshaler) Marshal(v interface{}) (b []byte, err error) { + buf := new(bytes.Buffer) + err = gob.NewEncoder(buf).Encode(v) + b = buf.Bytes() + return +} + +func (gobMarshaler) Unmarshal(data []byte, v interface{}) error { + return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v) +} diff --git a/pkg/db/page.go b/pkg/db/page.go new file mode 100644 index 0000000..a7722c3 --- /dev/null +++ b/pkg/db/page.go @@ -0,0 +1,42 @@ +package db + +type Article struct { + Title string `json:"title"` + LinkTitle string `json:"-"` + Search string `json:"search"` + Index template.HTML `json:"index"` + Text template.HTML `json:"text"` + MD string `json:"md"` + Created string `json:"created"` + Updated string `json:"updated"` + Public bool `json:"public"` +} + +type Permission int + +const ( + Public Permission = iota + Internal + Private +) + +type Page struct { + Title string `json:"title"` + Markdown string `json:"markdown"` + Index template.HTML `json:"index"` + Text template.HTML `json:"text"` + Created string `json:"created"` + Updated string `json:"updated"` + Perm Permission `json:"perm"` +} + +func (bs *BoltStore) GetPage(title string) (p Page, err error) { + err = bs.db.View(func(tx *bolt.Tx) error { + v := tx.Bucket([]byte(pageTable)).Get([]byte(username)) + if v == nil { + return errUserNotExists + } + return bs.Unmarshal(v, &u) + }) + return +} diff --git a/snippet.go b/pkg/db/snippet.go similarity index 99% rename from snippet.go rename to pkg/db/snippet.go index e267c3e..6ed1549 100644 --- a/snippet.go +++ b/pkg/db/snippet.go @@ -1,4 +1,4 @@ -package main +package db import ( "bytes" diff --git a/userdb.go b/pkg/db/user.go similarity index 72% rename from userdb.go rename to pkg/db/user.go index 83428b0..164bcae 100644 --- a/userdb.go +++ b/pkg/db/user.go @@ -1,88 +1,10 @@ -// Copyright (C) 2017 Marius Schellenberger - -package main +package db import ( - "bytes" - "encoding/gob" - "errors" "github.com/boltdb/bolt" "golang.org/x/crypto/bcrypt" - "golang.org/x/crypto/sha3" ) -type User struct { - Username string `json:"username"` - Password string `json:"password"` - Admin bool `json"admin"` - Locked int `json:"locked"` -} - -func validatePassword(pw string) (b []byte) { - b = []byte(pw) - if len(pw) <= 56 { - return - } - hash := sha3.New384() - hash.Write(b) - b = hash.Sum(nil) - return -} - -const ( - dbFile = "gowiki.db" - defUser = "admin" - defPassword = "gowiki" - userTable = "user" - snippetTable = "snippet" - bcryptCost = 13 -) - -var ( - errUserNotExists = dbErr("user does not exist") - errUserExists = dbErr("user already exist") -) - -func dbErr(i interface{}) error { - return fmt.Errorf("db: %s", i) -} - -type BoltStore struct { - Marshaler - db *bolt.DB -} - -func NewBoltStore() (bs *BoltStore, err error) { - db, err := bolt.Open(dbFile, 0666, nil) - if err != nil { - return - } - if err = db.Update(func(tx *bolt.Tx) error { - if _, err := tx.CreateBucketIfNotExists([]byte(userTable)); err != nil { - return err - } - if _, err := tx.CreateBucketIfNotExists([]byte(snippetTable)); err != nil { - return err - } - return nil - }); err != nil { - return - } - bs = &BoltStore{NewGOB(), db} - if !bs.UserExists(defUser) { - err = bs.CreateUser(defUser, defPassword, true) - if err != nil { - return - } - } - bs.UnlockUser(defUser) - return -} - -func (bs *BoltStore) Close() error { - return bs.db.Close() -} - func (bs *BoltStore) GetUsers() (users []User, err error) { users, err = bs.DumpUsers() for i := range users { @@ -300,25 +222,3 @@ func (bs *BoltStore) DeleteUser(username string) error { return tx.Bucket([]byte(userTable)).Delete([]byte(username)) }) } - -type Marshaler interface { - Marshal(v interface{}) ([]byte, error) - Unmarshal(data []byte, v interface{}) error -} - -type gobMarshaler struct{} - -func NewGOB() Marshaler { - return gobMarshaler{} -} - -func (gobMarshaler) Marshal(v interface{}) (b []byte, err error) { - buf := new(bytes.Buffer) - err = gob.NewEncoder(buf).Encode(v) - b = buf.Bytes() - return -} - -func (gobMarshaler) Unmarshal(data []byte, v interface{}) error { - return gob.NewDecoder(bytes.NewBuffer(data)).Decode(v) -} diff --git a/pkg/index/index.go b/pkg/index/index.go new file mode 100644 index 0000000..d7b0a79 --- /dev/null +++ b/pkg/index/index.go @@ -0,0 +1,112 @@ +// Copyright (C) 2017 Marius Schellenberger + +package main + +import ( + "fmt" + "os" + + "github.com/blevesearch/bleve" + "github.com/blevesearch/bleve/document" +) + +type IndexPage struct { + Title string `json:"title"` + Search string `json:"search"` +} + +func indexErr(i interface{}) error { + return fmt.Errorf("index: %s", i) +} + +type Index struct { + i bleve.Index +} + +func NewIndex(path string) (i *Index, err error) { + var bi bleve.Index + if _, err = os.Stat(path); os.IsNotExist(err) { + mapping := bleve.NewIndexMapping() + bi, err = bleve.New(path, mapping) + if err != nil { + err = indexErr(err) + return + } + i = &Index{index: bi} + _, err = i.Index(indexName, welcome, defUser, true) + if err != nil { + err = indexErr(err) + return + } + } else { + bi, err = bleve.Open(path) + if err != nil { + err = indexErr(err) + return + } + i = &Index{index: bi} + } + i.cache = NewIndexCache() + return +} + +func NewDumpIndex(path string) (i *Index, err error) { + bi, err := bleve.OpenUsing(path, map[string]interface{}{"read_only": true}) + if err != nil { + err = indexErr(err) + return + } + i = &Index{index: bi} + return +} + +func (i *Index) Close() error { + return i.i.Close() +} + +func (i *Index) Add(ip IndexPage) error { + doc, _ := i.i.Document(ip.Title) + if doc != nil { + err := i.Delete(ip.Title) + if err != nil { + return err + } + } + return i.i.Index(ip.Title, ip) +} + +func (i *Index) Delete(title string) error { + return i.i.Delete(title) +} + +type Result struct { + Title string + Text string +} + +func (i *Index) Search(search string) (results []Result, err error) { + req := bleve.NewSearchRequest(bleve.NewQueryStringQuery("title:" + search + " search:" + search)) + req.Highlight = bleve.NewHighlightWithStyle("html") + req.Size = maxSearchResult + res, err := i.i.Search(req) + if err != nil { + return + } + for _, hit := range res.Hits { + if hit == nil { + continue + } + title := hit.ID + text := "" + for fragField, frags := range hit.Fragments { + if fragField == "title" || fragField == "search" { + for _, f := range frags { + text += f + } + break + } + } + results = append(results, Result{title, text}) + } + return +} diff --git a/render.go b/pkg/index/render.go similarity index 90% rename from render.go rename to pkg/index/render.go index be2b360..40262f4 100644 --- a/render.go +++ b/pkg/index/render.go @@ -67,7 +67,9 @@ func render(text string) *rendered { } func spaceReplace(s string) string { - return strings.Replace(s, " ", "+", -1) + return whiteSpace.ReplaceAllString(s, "+") + //TODO remove + //return strings.Replace(s, " ", "+", -1) } func makeLinkTitle(t string) (newTitle string, title string) { @@ -141,3 +143,12 @@ func closeTag(j int) (ret string) { } return } + +func renderResult(res []Result) (html string) { + //TODO fmt.Sprintf ? + for r := range res { + html += `` + r.Title + "" + + `
` + r.Text + "

" + + } +} diff --git a/logger.go b/pkg/log/log.go similarity index 69% rename from logger.go rename to pkg/log/log.go index 75efbf0..660cd5f 100644 --- a/logger.go +++ b/pkg/log/log.go @@ -21,3 +21,15 @@ func newLogger(file string) { logger.SetOutput(f) log = logger.New(f, "", logger.LstdFlags) } + +func Println(v ...interface{}) { + log.Println(v...) +} + +func Printf(fmt string, v ...interface{}) { + log.Printf(fmt, v...) +} + +func Fatal(v ...interface{}) { + log.Fatal(v...) +} diff --git a/cache.go b/pkg/server/cache.go similarity index 100% rename from cache.go rename to pkg/server/cache.go diff --git a/context.go b/pkg/server/context.go similarity index 100% rename from context.go rename to pkg/server/context.go diff --git a/editor.go b/pkg/server/editor.go similarity index 100% rename from editor.go rename to pkg/server/editor.go diff --git a/handler.go b/pkg/server/handler.go similarity index 100% rename from handler.go rename to pkg/server/handler.go diff --git a/routes.go b/pkg/server/routes.go similarity index 100% rename from routes.go rename to pkg/server/routes.go diff --git a/server.go b/pkg/server/server.go similarity index 100% rename from server.go rename to pkg/server/server.go diff --git a/snippet_handler.go b/pkg/server/snippet_handler.go similarity index 100% rename from snippet_handler.go rename to pkg/server/snippet_handler.go diff --git a/templates.go b/pkg/server/templates.go similarity index 100% rename from templates.go rename to pkg/server/templates.go diff --git a/helper.go b/pkg/util/helper.go similarity index 100% rename from helper.go rename to pkg/util/helper.go diff --git a/templates/wikiEdit.html b/templates/wikiEdit.html index b30db18..d2b0375 100644 --- a/templates/wikiEdit.html +++ b/templates/wikiEdit.html @@ -27,13 +27,19 @@
-
- {{if .Data.Public}} - - {{else}} - - {{end}} +
+ + +
+
+ + +
+
+ + +
diff --git a/templates/wikiNew.html b/templates/wikiNew.html index c76850d..a0a994c 100644 --- a/templates/wikiNew.html +++ b/templates/wikiNew.html @@ -20,9 +20,19 @@
-
- +
+ + +
+
+ + +
+
+ + +
diff --git a/todo.txt b/todo.txt new file mode 100644 index 0000000..456b180 --- /dev/null +++ b/todo.txt @@ -0,0 +1,2 @@ +move search results check to db +move html generation to render