Coder Social home page Coder Social logo

yogthos / memory-hole Goto Github PK

View Code? Open in Web Editor NEW
259.0 21.0 25.0 1.99 MB

Memory Hole is a support issue organizer application

License: MIT License

Clojure 78.64% CSS 4.31% HTML 1.80% PLpgSQL 1.26% Dockerfile 0.16% Shell 0.21% JavaScript 10.43% TSQL 3.19%
issue-tracker collaboration notes organizer team web-app

memory-hole's Introduction

Memory Hole

When one knew that any document was due for destruction, or even when one saw a scrap of waste paper lying about, it was an automatic action to lift the flap of the nearest memory hole and drop it in, whereupon it would be whirled away on a current of warm air to the enormous furnaces which were hidden somewhere in the recesses of the building


Memory Hole is a support issue organizer. It's designed to provide a way to organize and search common support issues and their resolution.

1.0 Features

  • The app uses LDAP and/or internal table to handle authentication
  • Issues are ranked based on the number of views and last time of access
  • File attachments for issues
  • Issues are organized by user generated tags
  • Markdown with live preview for issues
  • Weighted full text search using PostgreSQL citext extension
  • Users can view/edit issues based on their group membership
  • If using LDAP, issues can be assigned to LDAP groups
  • LDAP groups can be aliased with user friendly names

Prerequisites

You will need the following to compile and run the application:

Running with Docker

mkdir memory-hole
cd memory-hole
curl -O https://raw.githubusercontent.com/yogthos/memory-hole/master/docker-compose.yml
docker-compose up

The app will be available at http://localhost:8000 once it starts.

Configuring the database

PostgreSQL

Follow these steps to configure the database for the application:

  1. Make sure you have the CITEXT extension installed on PostgreSQL.

  2. Run the psql command:

     psql -U <superuser|postgres user> -d postgres -h localhost
    
  3. Create the role role for accessing the database:

     CREATE ROLE memoryhole;
    
  4. Set the password for the role:

     \password memoryhole;
    
  5. Optionally, create a schema and grant the memoryhole role authorization:

     CREATE SCHEMA memoryhole AUTHORIZATION memoryhole;
     GRANT ALL ON SCHEMA memoryhole TO memoryhole;
     GRANT ALL ON ALL TABLES IN SCHEMA memoryhole TO memoryhole;
    
  6. Add the CITEXT extension to the schema:

     CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA memoryhole;
    
  7. Make sure memoryhole is allowed to login:

     ALTER ROLE "memoryhole" WITH LOGIN;
    
  8. Exit the shell

     \q
    

This setup should lead to similar :database-url (eg. on local machine).

:database-url "jdbc:postgresql://localhost/postgres?user=memoryhole&password=memoryhole"

H2

H2 DB can use various hosting scenarios, which are available on its feature list.

This setup can lead to following :database-url on local machine.

:database-url "jdbc:h2:~/memory-hole-dev"

When H2 DB is used for development or production, it needs to have properly set migratus :migration-dir pointing to H2 specific migrations for populating schema.

:migration-dir "migrations/h2"

Running during development

Create a profiles.clj file in the project directory with the configuration settings for the database. Optionally migrations directory and LDAP can be configured, e.g:

{:profiles/dev
 {:env
  {:database-url "jdbc:postgresql://localhost/postgres?user=memoryhole&password=memoryhole"
  :migration-dir "migrations/postgresql"
  ;;ldap is optional, will use internal table otherwise
  ;;Admin users (able to manage groups) defined by their sAMAccountName
  :ldap-admin-users ["my-ldap-sAMAccountName" "another-ldap-sAMAccountName"]
  ;;Or Admin Groups defined by their distinguished names
  :ldap-admin-groups ["CN=some-ldap-group,OU=foo123,DC=domain,DC=ca"]
  :ldap
  {:host
     {:address         "my-ldap-server.ca"
      :domain          "domain.ca"
      :port            389
      :connect-timeout (* 1000 5)
      :timeout         (* 1000 30)}}}}}

Run the migrations

lein run migrate

This will create the tables and add a default admin user, The default login is: admin/admin.

To start a web server for the application, run:

lein run

To compile ClojureScript front-end, run:

lein figwheel

Building for production

lein uberjar

This will produce target/uberjar/memory-hole.jar archive that can be run as follows:

java -Dconf=conf.edn -jar memory-hole.jar migrate
java -Dconf=conf.edn -jar memory-hole.jar

The conf.edn file should contain the configuration such as the database URL that will be used in production. The following options are available.

Database URL

:database-url "jdbc:postgresql://localhost/postgres?user=memoryhole&password=memoryhole"

Migration directory

Depending on selected DB backend, migration directory needs to be set, eg.

:migration-dir "migrations/postgresql"

HTTP Port

The HTTP port defaults to 3000, to set a custom port add the following key to the config:

:port 80

Session Configuration

The app defaults to using a server-side memory based session store.

The number of sessions before a memory session times out can be set using the :memory-session key as follows:

:memory-session
{:max-age 3600}

If you wish to use a cookie based memory store, then add a :cookie-session key to the configuration. The :cookie-session key should point to a map containing two optional key:

  • :key - a secret key used to encrypt the session cookie
  • :cookie-attrs - a map containing optional cookie attributes:
  • :http-only - restrict the cookie to HTTP if true (default)
  • :secure - restrict the cookie to HTTPS URLs if true
  • :max-age - the number of seconds until the cookie expires

An example configuration might look as follows:

:cookie-session
{:key "a 16-byte secret"
 :cookie-attrs
 {:secure  true
  :max-age 3600}}

LDAP Support

The LDAP connection configuration should be placed under the :ldap key as follows:

:ldap
  {:host
     {:address         "my-ldap-server.ca"
      :domain          "domain.ca"
      :port            389
      :connect-timeout (* 1000 5)
      :timeout         (* 1000 30)}}

There are two options for managing user groups when using LDAP, you can either assign admin users using the sAMAccountName, or specify groups that correspond to the memberOf key.

:ldap-admin-users ["my-ldap-sAMAccountName" "another-ldap-sAMAccountName"]
:ldap-admin-groups ["CN=some-ldap-group,OU=foo123,DC=domain,DC=ca"]

HTTPS Support

To enable HTTPS support in production add the the following configuration under the :ssl key:

:ssl
{:port 3001
 :keystore "keystore.jks"
 :keystore-pass "changeit"}

To disable HTTP access, set the :port to nil:

:port nil

Alternatively, you can front the app with Nginx in production. See here for details on configuring Nginx.

A complete conf.edn example:

{:database-url "jdbc:postgresql://localhost/postgres?user=memoryhole&password=memoryhole"
 :cookie-session
 {:key "a 16-byte secret"
  :cookie-attrs
  {:max-age 60}}
 :port nil
 :ssl
 {:port 3001
  :keystore "keystore.jks"
  :keystore-pass "changeit"}}

Nginx Proxy

The app can be proxied with Nginx to a custom path as follows:

server {
    listen ...;
    ...
    location /memory-hole {
        proxy_pass http://127.0.0.1:3000;
    }
    ...
}

You will then need to add the :app-context in the conf.edn file with the context:

{:database-url "jdbc:postgresql://localhost/postgres?user=memoryhole&password=memoryhole"
 :port 3000
 :app-context "/memory-hole"}

Acknowledgments

The original implementation of the tool was written by Ryan Baldwin. The app is based on the original schema and SQL queries.

License

Copyright © 2016 Dmitri Sotnikov

memory-hole's People

Contributors

celwell avatar haraldkoch avatar hpetru avatar jfifield avatar jhamrsky avatar jumarko avatar lacarmen avatar paulrd avatar svmbrown avatar yogthos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

memory-hole's Issues

Erro upon lein run migrate

I am using PG 8.4. I get the following:


➜  memory-hole git:(master) lein run migrate 
2016-08-29 09:34:53,311 [main] INFO  migratus.core - Starting migrations 
2016-08-29 09:34:53,736 [main] INFO  migratus.database - creating migration table 'schema_migrations' 
2016-08-29 09:34:53,876 [main] INFO  migratus.core - Running up for [100 200 400 500 600 700] 
2016-08-29 09:34:53,878 [main] INFO  migratus.core - Up 100-users 
2016-08-29 09:34:53,921 [main] ERROR migratus.database - failed to execute command:
 CREATE EXTENSION IF NOT EXISTS citext WITH SCHEMA public;


org.postgresql.util.PSQLException: ERROR: syntax error at or near "EXTENSION"
  Position: 8
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2310) ~[postgresql-9.4.1209.jar:9.4.1209]
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2023) ~[postgresql-9.4.1209.jar:9.4.1209]
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:217) ~[postgresql-9.4.1209.jar:9.4.1209]
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:421) ~[postgresql-9.4.1209.jar:9.4.1209]
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:166) ~[postgresql-9.4.1209.jar:9.4.1209]
    at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:137) ~[postgresql-9.4.1209.jar:9.4.1209]
    at clojure.java.jdbc$db_do_execute_prepared_statement$fn__34004.invoke(jdbc.clj:784) ~[na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:637) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:598) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585) [na:na]
    at clojure.java.jdbc$db_do_execute_prepared_statement.invokeStatic(jdbc.clj:783) ~[na:na]
    at clojure.java.jdbc$db_do_execute_prepared_statement.invoke(jdbc.clj:778) ~[na:na]
    at clojure.java.jdbc$db_do_prepared.invokeStatic(jdbc.clj:814) ~[na:na]
    at clojure.java.jdbc$db_do_prepared.invoke(jdbc.clj:795) ~[na:na]
    at clojure.java.jdbc$db_do_prepared.invokeStatic(jdbc.clj:802) ~[na:na]
    at clojure.java.jdbc$db_do_prepared.invoke(jdbc.clj:795) ~[na:na]
    at migratus.database$up_STAR_$fn__39711$fn__39720.invoke(database.clj:76) ~[na:na]
    at migratus.database$up_STAR_$fn__39711.invoke(database.clj:75) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:613) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:598) [na:na]
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585) [na:na]
    at migratus.database$up_STAR_.invokeStatic(database.clj:68) [na:na]
    at migratus.database$up_STAR_.invoke(database.clj:67) [na:na]
    at migratus.database.Migration.up(database.clj:206) [na:na]
    at migratus.core$up_STAR_.invokeStatic(core.clj:47) [na:na]
    at migratus.core$up_STAR_.invoke(core.clj:45) [na:na]
    at migratus.core$migrate_up_STAR_.invokeStatic(core.clj:55) [na:na]
    at migratus.core$migrate_up_STAR_.invoke(core.clj:49) [na:na]
    at migratus.core$migrate_STAR_.invokeStatic(core.clj:61) [na:na]
    at migratus.core$migrate_STAR_.invoke(core.clj:59) [na:na]
    at migratus.core$run.invokeStatic(core.clj:25) [na:na]
    at migratus.core$run.invoke(core.clj:21) [na:na]
    at migratus.core$migrate.invokeStatic(core.clj:66) [na:na]
    at migratus.core$migrate.invoke(core.clj:63) [na:na]
    at luminus_migrations.core$migrate.invokeStatic(core.clj:48) [na:na]
    at luminus_migrations.core$migrate.invoke(core.clj:25) [na:na]
    at memory_hole.core$_main.invokeStatic(core.clj:59) [na:na]
    at memory_hole.core$_main.doInvoke(core.clj:54) [na:na]
    at clojure.lang.RestFn.invoke(RestFn.java:408) [clojure-1.8.0.jar:na]
    at clojure.lang.Var.invoke(Var.java:379) [clojure-1.8.0.jar:na]
    at user$eval40423.invokeStatic(form-init489971274304859740.clj:1) [na:na]
    at user$eval40423.invoke(form-init489971274304859740.clj:1) [na:na]
    at clojure.lang.Compiler.eval(Compiler.java:6927) [clojure-1.8.0.jar:na]
    at clojure.lang.Compiler.eval(Compiler.java:6917) [clojure-1.8.0.jar:na]
    at clojure.lang.Compiler.load(Compiler.java:7379) [clojure-1.8.0.jar:na]
    at clojure.lang.Compiler.loadFile(Compiler.java:7317) [clojure-1.8.0.jar:na]
    at clojure.main$load_script.invokeStatic(main.clj:275) [clojure-1.8.0.jar:na]
    at clojure.main$init_opt.invokeStatic(main.clj:277) [clojure-1.8.0.jar:na]
    at clojure.main$init_opt.invoke(main.clj:277) [clojure-1.8.0.jar:na]
    at clojure.main$initialize.invokeStatic(main.clj:308) [clojure-1.8.0.jar:na]
    at clojure.main$null_opt.invokeStatic(main.clj:342) [clojure-1.8.0.jar:na]
    at clojure.main$null_opt.invoke(main.clj:339) [clojure-1.8.0.jar:na]
    at clojure.main$main.invokeStatic(main.clj:421) [clojure-1.8.0.jar:na]
    at clojure.main$main.doInvoke(main.clj:384) [clojure-1.8.0.jar:na]
    at clojure.lang.RestFn.invoke(RestFn.java:421) [clojure-1.8.0.jar:na]
    at clojure.lang.Var.invoke(Var.java:383) [clojure-1.8.0.jar:na]
    at clojure.lang.AFn.applyToHelper(AFn.java:156) [clojure-1.8.0.jar:na]
    at clojure.lang.Var.applyTo(Var.java:700) [clojure-1.8.0.jar:na]
    at clojure.main.main(main.java:37) [clojure-1.8.0.jar:na]
2016-08-29 09:34:53,926 [main] INFO  migratus.core - Ending migrations 
Exception in thread "main" org.postgresql.util.PSQLException: ERROR: syntax error at or near "EXTENSION"
  Position: 8, compiling:(/tmp/form-init489971274304859740.clj:1:72)
    at clojure.lang.Compiler.load(Compiler.java:7391)
    at clojure.lang.Compiler.loadFile(Compiler.java:7317)
    at clojure.main$load_script.invokeStatic(main.clj:275)
    at clojure.main$init_opt.invokeStatic(main.clj:277)
    at clojure.main$init_opt.invoke(main.clj:277)
    at clojure.main$initialize.invokeStatic(main.clj:308)
    at clojure.main$null_opt.invokeStatic(main.clj:342)
    at clojure.main$null_opt.invoke(main.clj:339)
    at clojure.main$main.invokeStatic(main.clj:421)
    at clojure.main$main.doInvoke(main.clj:384)
    at clojure.lang.RestFn.invoke(RestFn.java:421)
    at clojure.lang.Var.invoke(Var.java:383)
    at clojure.lang.AFn.applyToHelper(AFn.java:156)
    at clojure.lang.Var.applyTo(Var.java:700)
    at clojure.main.main(main.java:37)
Caused by: org.postgresql.util.PSQLException: ERROR: syntax error at or near "EXTENSION"
  Position: 8
    at org.postgresql.core.v3.QueryExecutorImpl.receiveErrorResponse(QueryExecutorImpl.java:2310)
    at org.postgresql.core.v3.QueryExecutorImpl.processResults(QueryExecutorImpl.java:2023)
    at org.postgresql.core.v3.QueryExecutorImpl.execute(QueryExecutorImpl.java:217)
    at org.postgresql.jdbc.PgStatement.execute(PgStatement.java:421)
    at org.postgresql.jdbc.PgPreparedStatement.executeWithFlags(PgPreparedStatement.java:166)
    at org.postgresql.jdbc.PgPreparedStatement.executeUpdate(PgPreparedStatement.java:137)
    at clojure.java.jdbc$db_do_execute_prepared_statement$fn__34004.invoke(jdbc.clj:784)
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:637)
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585)
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:598)
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585)
    at clojure.java.jdbc$db_do_execute_prepared_statement.invokeStatic(jdbc.clj:783)
    at clojure.java.jdbc$db_do_execute_prepared_statement.invoke(jdbc.clj:778)
    at clojure.java.jdbc$db_do_prepared.invokeStatic(jdbc.clj:814)
    at clojure.java.jdbc$db_do_prepared.invoke(jdbc.clj:795)
    at clojure.java.jdbc$db_do_prepared.invokeStatic(jdbc.clj:802)
    at clojure.java.jdbc$db_do_prepared.invoke(jdbc.clj:795)
    at migratus.database$up_STAR_$fn__39711$fn__39720.invoke(database.clj:76)
    at migratus.database$up_STAR_$fn__39711.invoke(database.clj:75)
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:613)
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585)
    at clojure.java.jdbc$db_transaction_STAR_.invokeStatic(jdbc.clj:598)
    at clojure.java.jdbc$db_transaction_STAR_.invoke(jdbc.clj:585)
    at migratus.database$up_STAR_.invokeStatic(database.clj:68)
    at migratus.database$up_STAR_.invoke(database.clj:67)
    at migratus.database.Migration.up(database.clj:206)
    at migratus.core$up_STAR_.invokeStatic(core.clj:47)
    at migratus.core$up_STAR_.invoke(core.clj:45)
    at migratus.core$migrate_up_STAR_.invokeStatic(core.clj:55)
    at migratus.core$migrate_up_STAR_.invoke(core.clj:49)
    at migratus.core$migrate_STAR_.invokeStatic(core.clj:61)
    at migratus.core$migrate_STAR_.invoke(core.clj:59)
    at migratus.core$run.invokeStatic(core.clj:25)
    at migratus.core$run.invoke(core.clj:21)
    at migratus.core$migrate.invokeStatic(core.clj:66)
    at migratus.core$migrate.invoke(core.clj:63)
    at luminus_migrations.core$migrate.invokeStatic(core.clj:48)
    at luminus_migrations.core$migrate.invoke(core.clj:25)
    at memory_hole.core$_main.invokeStatic(core.clj:59)
    at memory_hole.core$_main.doInvoke(core.clj:54)
    at clojure.lang.RestFn.invoke(RestFn.java:408)
    at clojure.lang.Var.invoke(Var.java:379)
    at user$eval40423.invokeStatic(form-init489971274304859740.clj:1)
    at user$eval40423.invoke(form-init489971274304859740.clj:1)
    at clojure.lang.Compiler.eval(Compiler.java:6927)
    at clojure.lang.Compiler.eval(Compiler.java:6917)
    at clojure.lang.Compiler.load(Compiler.java:7379)
    ... 14 more

add pagination

When total issues is greater than the page size, provide navigation to load different offset from the issue table.

add support for groups

This would add the ability to associate the issue with a group as well as a tag. User would only see issues for the particular groups the user belongs to. The goal is to allow multiple teams to use the application and only see issues relevant to them.

Bug: Issues sometimes appear unfiltered even though a tag is selected

Steps to reproduce:

  1. Select a tag
  2. Click on an issue
  3. Navigate back to the home page by clicking "Issues" in the top left corner

You will see that the tag you selected previously is still selected but the list of issues is no longer filtered. Issue filtering is triggered by a url change to /issues/tag-name (ie. it happens on the backend) so I suggest we filter in the re-frame subscription for :visible-issues to correct this without having to do weird routing things.

Error: running lein run migrate

Dear yogthos,

I am new to Clojurescript and trying to learn from your project but when I run command "lein run migrate" I get a error information as in attached file.
errlog.txt
Please help me out.

Best regards,
Thinh P. Tran

add support for groups

Add a group table, and allow users to view issues based on a group. Each issue should be assigned to a group, and when user switches groups they should see the issues for that group.

ldap server address and domain are not necessarily the same

the LDAP authenticator uses the :address field of the LDAP config as the domain name to append to the username. In some environments, we need separate entries for the DNS hostname of the LDAP server, and the domain name used for authentication.

Wrong order of recently viewed issues

Hello,

I think that recently viewed issues should be ordered by ASC instead of DESC - I would expect last viewed / created issue to be on top of the page, not on bottom. It was ok until f840fe0.

Furthermore, are we sure that last_viewed_date column name corresponds to its use? I can see this value to be updated when issue is created and updated, therefore it seems that:

  • last_viewed_date should be last_modified_date
  • recently-viewed-issues should be recently-modified-issues
  • should be set when issue is created (initialization) and modified (update)

handle duplicate filename on upload

Uploading two files with the same filename (to the same issue) causes sql error. Error should either be caught and result in 4xx error (rather than 500) or filename should be changed.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.