Grooming, new CLI options.
This commit is contained in:
parent
802812e99f
commit
f0295031e9
13
src/column.cr
Normal file
13
src/column.cr
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
require "uuid"
|
||||||
|
|
||||||
|
class Column
|
||||||
|
JSON.mapping({
|
||||||
|
id: String,
|
||||||
|
name: String
|
||||||
|
})
|
||||||
|
|
||||||
|
def initialize(@name)
|
||||||
|
@id = UUID.random.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
233
src/main.cr
233
src/main.cr
@ -4,186 +4,40 @@ require "file_utils"
|
|||||||
|
|
||||||
require "authd"
|
require "authd"
|
||||||
|
|
||||||
class Task
|
require "./project.cr"
|
||||||
JSON.mapping({
|
require "./requests.cr"
|
||||||
id: String,
|
|
||||||
author: Int32,
|
|
||||||
title: String,
|
|
||||||
description: String,
|
|
||||||
column: String,
|
|
||||||
assigned_to: Int32?
|
|
||||||
})
|
|
||||||
|
|
||||||
def initialize(@title, @author, @description, @column)
|
authd_key = nil : String?
|
||||||
@id = UUID.random.to_s
|
storage_directory = "./projects"
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Column
|
# It’ll get replaced later. But FIXME, this is only a temporary workaround.
|
||||||
JSON.mapping({
|
authd = nil.unsafe_as AuthD::Client
|
||||||
id: String,
|
|
||||||
name: String
|
|
||||||
})
|
|
||||||
|
|
||||||
def initialize(@name)
|
|
||||||
@id = UUID.random.to_s
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Project
|
|
||||||
JSON.mapping({
|
|
||||||
id: String,
|
|
||||||
name: String,
|
|
||||||
columns: Array(Column),
|
|
||||||
tasks: Array(Task),
|
|
||||||
color: {
|
|
||||||
type: String,
|
|
||||||
default: "dark"
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
def initialize(@name)
|
|
||||||
@id = UUID.random.to_s
|
|
||||||
|
|
||||||
@columns = [Column.new "To do"]
|
|
||||||
@tasks = [] of Task
|
|
||||||
|
|
||||||
@color = "dark"
|
|
||||||
|
|
||||||
write!
|
|
||||||
end
|
|
||||||
|
|
||||||
def write!
|
|
||||||
Dir.mkdir_p "./projects/#{@id}"
|
|
||||||
|
|
||||||
File.write "./projects/#{@id}/project.json.new", to_json
|
|
||||||
FileUtils.mv "./projects/#{@id}/project.json.new",
|
|
||||||
"./projects/#{@id}/project.json"
|
|
||||||
end
|
|
||||||
|
|
||||||
# FIXME: Transform that exception somehow.
|
|
||||||
# FIXME: Update to not read everything.
|
|
||||||
def self.get_from_id(id)
|
|
||||||
self.all.find(&.id.==(id)).not_nil!
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.all : Array(Project)
|
|
||||||
# FIXME: switch to an index file per project in a storage directory.
|
|
||||||
Dir.children("./projects").compact_map do |filename|
|
|
||||||
begin
|
|
||||||
Project.from_json File.read "./projects/#{filename}/project.json"
|
|
||||||
rescue
|
|
||||||
end
|
|
||||||
end
|
|
||||||
rescue
|
|
||||||
[] of Project
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::Login
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
login: String,
|
|
||||||
password: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::Tasks
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::NewProject
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
name: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::EditProject
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
name: String?,
|
|
||||||
color: String?
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::NewColumn
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
name: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::NewTask
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
column: String,
|
|
||||||
title: String,
|
|
||||||
description: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::EditTask
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
task: String,
|
|
||||||
column: String?,
|
|
||||||
title: String?,
|
|
||||||
description: String?,
|
|
||||||
assigned_to: Int32?
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::DeleteTask
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
task: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::EditColumn
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
column: String,
|
|
||||||
name: String?
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::DeleteColumn
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
project: String,
|
|
||||||
column: String
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
class Requests::GetUser
|
|
||||||
JSON.mapping({
|
|
||||||
type: String,
|
|
||||||
uid: Int32
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
authd = AuthD::Client.new
|
|
||||||
|
|
||||||
Kemal.config.extra_options do |parser|
|
Kemal.config.extra_options do |parser|
|
||||||
parser.on "-k file", "--jwt-key file", "Provides the JWT key for authd." do |file|
|
parser.on "-k file", "--jwt-key file", "Provides the JWT key for authd." do |file|
|
||||||
authd.key = File.read(file).chomp
|
authd_key = File.read(file).chomp
|
||||||
|
end
|
||||||
|
|
||||||
|
parser.on "-S dir", "--storage dir", "Provides a directory in which to store kanband’s data and projects." do |dir|
|
||||||
|
storage_directory = dir
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
sockets = [] of HTTP::WebSocket
|
sockets = [] of HTTP::WebSocket
|
||||||
users = {} of HTTP::WebSocket => AuthD::User
|
users = {} of HTTP::WebSocket => AuthD::User
|
||||||
|
|
||||||
|
get "/" do
|
||||||
|
File.read "index.html"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/style.css" do
|
||||||
|
File.read "style.css"
|
||||||
|
end
|
||||||
|
|
||||||
|
get "/main.js" do
|
||||||
|
File.read "main.js"
|
||||||
|
end
|
||||||
|
|
||||||
ws "/socket" do |socket|
|
ws "/socket" do |socket|
|
||||||
socket.on_close do
|
socket.on_close do
|
||||||
users.delete socket
|
users.delete socket
|
||||||
@ -220,7 +74,7 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
socket.send({
|
socket.send({
|
||||||
type: "list-projects",
|
type: "list-projects",
|
||||||
projects: Project.all
|
projects: Project.all storage_directory
|
||||||
}.to_json)
|
}.to_json)
|
||||||
when "new-project"
|
when "new-project"
|
||||||
user = users[socket] # FIXME: make it an authentication error
|
user = users[socket] # FIXME: make it an authentication error
|
||||||
@ -229,6 +83,8 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
project = Project.new request.name
|
project = Project.new request.name
|
||||||
|
|
||||||
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
type: "project",
|
type: "project",
|
||||||
@ -239,7 +95,7 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::Tasks.from_json message_as_s
|
request = Requests::Tasks.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
|
|
||||||
socket.send({
|
socket.send({
|
||||||
type: "project",
|
type: "project",
|
||||||
@ -250,7 +106,7 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::EditProject.from_json message_as_s
|
request = Requests::EditProject.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
|
|
||||||
if name = request.name
|
if name = request.name
|
||||||
project.name = name
|
project.name = name
|
||||||
@ -260,7 +116,7 @@ ws "/socket" do |socket|
|
|||||||
project.color = color
|
project.color = color
|
||||||
end
|
end
|
||||||
|
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -272,10 +128,10 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::NewColumn.from_json message_as_s
|
request = Requests::NewColumn.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
|
|
||||||
project.columns << Column.new request.name
|
project.columns << Column.new request.name
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -287,12 +143,12 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::NewTask.from_json message_as_s
|
request = Requests::NewTask.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
column = project.columns.find(&.id.==(request.column)).not_nil!
|
column = project.columns.find(&.id.==(request.column)).not_nil!
|
||||||
|
|
||||||
project.tasks << Task.new request.title, user.uid, request.description, column.id
|
project.tasks << Task.new request.title, user.uid, request.description, column.id
|
||||||
|
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -304,7 +160,7 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::EditTask.from_json message_as_s
|
request = Requests::EditTask.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
task = project.tasks.find(&.id.==(request.task)).not_nil!
|
task = project.tasks.find(&.id.==(request.task)).not_nil!
|
||||||
|
|
||||||
if column = request.column
|
if column = request.column
|
||||||
@ -329,7 +185,7 @@ ws "/socket" do |socket|
|
|||||||
task.assigned_to = assigned_to
|
task.assigned_to = assigned_to
|
||||||
end
|
end
|
||||||
|
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -341,11 +197,11 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::DeleteTask.from_json message_as_s
|
request = Requests::DeleteTask.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
|
|
||||||
project.tasks.select! &.id.!=(request.task)
|
project.tasks.select! &.id.!=(request.task)
|
||||||
|
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -357,7 +213,7 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
request = Requests::EditColumn.from_json message_as_s
|
request = Requests::EditColumn.from_json message_as_s
|
||||||
|
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
column = project.columns.find(&.id.==(request.column)).not_nil!
|
column = project.columns.find(&.id.==(request.column)).not_nil!
|
||||||
|
|
||||||
if name = request.name
|
if name = request.name
|
||||||
@ -366,7 +222,7 @@ ws "/socket" do |socket|
|
|||||||
|
|
||||||
puts project.columns.to_json
|
puts project.columns.to_json
|
||||||
|
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -377,12 +233,12 @@ ws "/socket" do |socket|
|
|||||||
user = users[socket]
|
user = users[socket]
|
||||||
|
|
||||||
request = Requests::DeleteColumn.from_json message_as_s
|
request = Requests::DeleteColumn.from_json message_as_s
|
||||||
project = Project.get_from_id request.project
|
project = Project.get_from_id request.project, storage_directory
|
||||||
|
|
||||||
project.columns.select! &.id.!=(request.column)
|
project.columns.select! &.id.!=(request.column)
|
||||||
project.tasks.select! &.column.!=(request.column)
|
project.tasks.select! &.column.!=(request.column)
|
||||||
|
|
||||||
project.write!
|
project.write! storage_directory
|
||||||
|
|
||||||
# FIXME: Only notify concerned users.
|
# FIXME: Only notify concerned users.
|
||||||
sockets.each &.send({
|
sockets.each &.send({
|
||||||
@ -402,5 +258,10 @@ ws "/socket" do |socket|
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
Kemal.run
|
Kemal.run do
|
||||||
|
authd = AuthD::Client.new
|
||||||
|
|
||||||
|
key = authd_key
|
||||||
|
authd.key = key if key.is_a? String
|
||||||
|
end
|
||||||
|
|
||||||
|
54
src/project.cr
Normal file
54
src/project.cr
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
require "uuid"
|
||||||
|
require "file_utils"
|
||||||
|
|
||||||
|
require "./task.cr"
|
||||||
|
require "./column.cr"
|
||||||
|
|
||||||
|
class Project
|
||||||
|
JSON.mapping({
|
||||||
|
id: String,
|
||||||
|
name: String,
|
||||||
|
columns: Array(Column),
|
||||||
|
tasks: Array(Task),
|
||||||
|
color: {
|
||||||
|
type: String,
|
||||||
|
default: "dark"
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
def initialize(@name)
|
||||||
|
@id = UUID.random.to_s
|
||||||
|
|
||||||
|
@columns = [Column.new "To do"]
|
||||||
|
@tasks = [] of Task
|
||||||
|
|
||||||
|
@color = "dark"
|
||||||
|
end
|
||||||
|
|
||||||
|
def write!(storage)
|
||||||
|
Dir.mkdir_p "#{storage}/#{@id}"
|
||||||
|
|
||||||
|
File.write "#{storage}/#{@id}/project.json.new", to_json
|
||||||
|
FileUtils.mv "#{storage}/#{@id}/project.json.new",
|
||||||
|
"#{storage}/#{@id}/project.json"
|
||||||
|
end
|
||||||
|
|
||||||
|
# FIXME: Transform that exception somehow.
|
||||||
|
# FIXME: Update to not read everything.
|
||||||
|
def self.get_from_id(id, storage)
|
||||||
|
self.all(storage).find(&.id.==(id)).not_nil!
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.all(storage) : Array(Project)
|
||||||
|
# FIXME: switch to an index file per project in a storage directory.
|
||||||
|
Dir.children(storage).compact_map do |filename|
|
||||||
|
begin
|
||||||
|
Project.from_json File.read "#{storage}/#{filename}/project.json"
|
||||||
|
rescue
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue
|
||||||
|
[] of Project
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
95
src/requests.cr
Normal file
95
src/requests.cr
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
require "json"
|
||||||
|
|
||||||
|
class Requests::Login
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
login: String,
|
||||||
|
password: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::Tasks
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::NewProject
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
name: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::EditProject
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
name: String?,
|
||||||
|
color: String?
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::NewColumn
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
name: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::NewTask
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
column: String,
|
||||||
|
title: String,
|
||||||
|
description: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::EditTask
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
task: String,
|
||||||
|
column: String?,
|
||||||
|
title: String?,
|
||||||
|
description: String?,
|
||||||
|
assigned_to: Int32?
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::DeleteTask
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
task: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::EditColumn
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
column: String,
|
||||||
|
name: String?
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::DeleteColumn
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
project: String,
|
||||||
|
column: String
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
class Requests::GetUser
|
||||||
|
JSON.mapping({
|
||||||
|
type: String,
|
||||||
|
uid: Int32
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
17
src/task.cr
Normal file
17
src/task.cr
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
require "uuid"
|
||||||
|
|
||||||
|
class Task
|
||||||
|
JSON.mapping({
|
||||||
|
id: String,
|
||||||
|
author: Int32,
|
||||||
|
title: String,
|
||||||
|
description: String,
|
||||||
|
column: String,
|
||||||
|
assigned_to: Int32?
|
||||||
|
})
|
||||||
|
|
||||||
|
def initialize(@title, @author, @description, @column)
|
||||||
|
@id = UUID.random.to_s
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
Loading…
Reference in New Issue
Block a user