aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Duncan <pabs@pablotron.org>2016-03-08 05:06:34 -0500
committerPaul Duncan <pabs@pablotron.org>2016-03-08 05:06:34 -0500
commit2c60a34cbe95d34950977e59d9316a1885b4fd6f (patch)
treecd510b6bf03e9eb9ebdccd5ec4a4f92acfdc3669
parent490464d7d07e94b6ee69bbbc4b290a2b0e3a0975 (diff)
downloadold-guff-2c60a34cbe95d34950977e59d9316a1885b4fd6f.tar.bz2
old-guff-2c60a34cbe95d34950977e59d9316a1885b4fd6f.zip
reorganize api
-rw-r--r--src/guff/api-content-type.cr12
-rw-r--r--src/guff/api-docs-html-view.cr14
-rw-r--r--src/guff/api-handler.cr13
-rw-r--r--src/guff/api-methods.cr591
-rw-r--r--src/guff/api/content-type.cr10
-rw-r--r--src/guff/api/dir.cr23
-rw-r--r--src/guff/api/file.cr23
-rw-r--r--src/guff/api/methods.cr366
-rw-r--r--src/guff/api/post.cr80
-rw-r--r--src/guff/api/site.cr39
-rw-r--r--src/guff/api/tag.cr21
-rw-r--r--src/guff/api/test.cr28
-rw-r--r--src/guff/api/util.cr31
13 files changed, 638 insertions, 613 deletions
diff --git a/src/guff/api-content-type.cr b/src/guff/api-content-type.cr
deleted file mode 100644
index d6e1f83..0000000
--- a/src/guff/api-content-type.cr
+++ /dev/null
@@ -1,12 +0,0 @@
-module Guff
- module APIContentType
- CONTENT_TYPES = {
- "development": "text/html; charset=utf-8",
- "production": "application/json; charset=utf8",
- }
-
- private def get_content_type
- CONTENT_TYPES[@models.config["environment"]]
- end
- end
-end
diff --git a/src/guff/api-docs-html-view.cr b/src/guff/api-docs-html-view.cr
index 5fdbd86..c0db5aa 100644
--- a/src/guff/api-docs-html-view.cr
+++ b/src/guff/api-docs-html-view.cr
@@ -1,7 +1,7 @@
require "html"
require "ecr/macros"
require "./page-html-view"
-require "./api-methods"
+require "./api/methods"
module Guff
class APIDocsHTMLView
@@ -14,20 +14,20 @@ module Guff
end
private def namespaces
- APIMethods::API.keys.sort
+ API::Methods::METHODS.keys.sort
end
private def methods(
namespace : String
)
- APIMethods::API[namespace].keys.sort
+ API::Methods::METHODS[namespace].keys.sort
end
private def method_text(
namespace : String,
method : String
)
- APIMethods::API[namespace][method][:text] as String
+ API::Methods::METHODS[namespace][method][:text] as String
end
private def method_args(
@@ -35,7 +35,7 @@ module Guff
method : String
)
if method_has_args?(namespace, method)
- args = APIMethods::API[namespace][method][:args] as \
+ args = API::Methods::METHODS[namespace][method][:args] as \
Hash(String, Hash(Symbol, String|Symbol|Bool)) | Nil
args.keys.sort
else
@@ -49,7 +49,7 @@ module Guff
name : String
) : String
if method_has_args?(namespace, method)
- arg = APIMethods::API[namespace][method][:args][name] as\
+ arg = API::Methods::METHODS[namespace][method][:args][name] as\
Hash(Symbol, String|Symbol|Bool)
arg[:text] as String
else
@@ -61,7 +61,7 @@ module Guff
namespace : String,
method : String
)
- APIMethods::API[namespace][method].has_key?(:args)
+ API::Methods::METHODS[namespace][method].has_key?(:args)
end
def h(s : String)
diff --git a/src/guff/api-handler.cr b/src/guff/api-handler.cr
index 370c8f6..4d4a996 100644
--- a/src/guff/api-handler.cr
+++ b/src/guff/api-handler.cr
@@ -1,6 +1,6 @@
require "json"
require "./handler"
-require "./api-methods"
+require "./api/*"
private macro define_method_calls(hash)
case namespace
@@ -26,8 +26,15 @@ end
module Guff
class APIHandler < Handler
- include APIMethods
- include APIContentType
+ include API::Methods
+ include API::ContentType
+ include API::Util
+ include API::PostAPI
+ include API::DirAPI
+ include API::FileAPI
+ include API::TagAPI
+ include API::SiteAPI
+ include API::TestAPI
PATH_RE = %r{
^/api
diff --git a/src/guff/api-methods.cr b/src/guff/api-methods.cr
deleted file mode 100644
index 334be88..0000000
--- a/src/guff/api-methods.cr
+++ /dev/null
@@ -1,591 +0,0 @@
-require "json"
-require "./handler"
-
-module Guff
- module APIMethods
- API = {
- "post": {
- "get_posts": {
- text: "Get posts matching query.",
-
- args: {
- "q": {
- text: "Search string.",
- type: :text,
- required: false,
- default: "",
- },
-
- "page": {
- text: "Page number",
- type: :int,
- required: false,
- default: "1",
- },
-
- "tags": {
- text: "Comma-separated list of tags (union)",
- # type: :tag_list,
- type: :json,
- required: false,
- default: "",
- },
-
- "sort": {
- text: "Sort order of results",
- type: :sort,
- required: false,
- default: "date,desc",
- },
-
- "cols": {
- text: "Column list",
- type: :text,
- required: false,
- },
- },
- },
-
- "add_post": {
- text: "Create new post.",
- args: {
- "name": {
- text: "Post title.",
- type: :text,
- required: true,
- },
-
- "slug": {
- text: "Post slug.",
- type: :slug,
- required: true,
- },
-
- "body": {
- text: "Post body.",
- type: :text,
- required: true,
- },
-
- "tags": {
- text: "Post tags.",
- # type: :tag_list,
- type: :json,
- required: false,
- default: "",
- },
- },
- },
-
- "update_post": {
- text: "Update existing post.",
- args: {
- "name": {
- text: "Post title.",
- type: :text,
- required: false,
- },
-
- "slug": {
- text: "Post slug.",
- type: :slug,
- required: false,
- },
-
- "body": {
- text: "Post body.",
- type: :text,
- required: false,
- },
-
- "tags": {
- text: "Post tags.",
- type: :json,
- required: false,
- },
-
- "posted": {
- text: "Is this post posted?",
- type: :bool,
- required: false,
- },
- },
- },
-
- "remove_posts": {
- text: "Remove existing posts.",
-
- args: {
- "post_ids": {
- text: "Post IDs.",
- type: :int_list,
- required: false,
- default: "",
- },
-
- "tags": {
- text: "Post tags.",
- # type: :tag_list,
- type: :json,
- required: false,
- default: "",
- },
- },
- },
-
- "set_tags": {
- text: "Set tags for posts.",
-
- args: {
- "post_ids": {
- text: "Post IDs.",
- type: :int_list,
- required: true,
- },
- },
- },
- },
-
- "dir": {
- "add": {
- text: "Create new directory",
-
- args: {
- "path": {
- text: "Path to new directory",
- type: :path,
- required: true,
- },
- },
- },
-
- "remove": {
- text: "Remove directory",
-
- args: {
- "path": {
- text: "Path to existing directory",
- type: :path,
- required: true,
- },
- },
- },
- },
-
- "file": {
- "add": {
- text: "Upload new file",
-
- args: {
- "path": {
- text: "Destination file path",
- type: :path,
- required: true,
- },
- },
- },
-
- "remove": {
- text: "Remove existing file",
-
- args: {
- "path": {
- text: "Destination file path",
- type: :path,
- required: true,
- },
- },
- },
- },
-
- "tag": {
- "get_tags": {
- text: "Get list of tags",
- },
-
- "remove_tags": {
- text: "Remove set of tags",
-
- args: {
- "tags": {
- text: "Tags to remove.",
- # type: :tag_list,
- type: :json,
- required: false,
- default: "",
- },
- },
- },
- },
-
- "site": {
- "add_site": {
- text: "Create a new site",
-
- args: {
- "name": {
- text: "Name of this site",
- type: :text,
- required: true,
- },
-
- "default": {
- text: "Is this the default site?",
- type: :bool,
- required: false,
- default: "f",
- },
-
- "domains": {
- text: "Array of domain matches.",
- type: :domain_list,
- required: true,
- },
- },
- },
-
- "remove_sites": {
- text: "Remove one or more existing sites",
-
- args: {
- "site_ids": {
- text: "IDs of sites to remove",
- type: :int_list,
- required: true,
- },
- },
- },
-
- "set_default": {
- text: "Set new default site",
-
- args: {
- "site_id": {
- text: "ID of new default site",
- type: :int,
- required: true,
- },
- },
- },
-
- "set_domains": {
- text: "Set domains for given site",
-
- args: {
- "site_id": {
- text: "ID of site",
- type: :int,
- required: true,
- },
-
- "domains": {
- text: "Domains for this site.",
- type: :domain_list,
- required: true,
- },
- },
- },
- },
-
- "test": {
- "version": {
- text: "Get version",
- },
-
- "get_posts": {
- text: "Test get posts",
- },
-
- "error": {
- text: "Test error response",
- },
- }
- }
-
- TYPE_CHECKS = {
- text: /.*/,
- slug: /^[a-z0-9\.-]+$/,
- int: /^\d+$/,
- int_list: /^\d+(?:,\d+)*$/,
- sort: /^[a-z0-9_]+,(?:asc|desc)$/,
-
- # FIXME: lock these down more
- json: /.*/,
- path: /^[^\/].*[^\/]$/,
- }
-
- private def get_method_args(
- params : HTTP::Params,
- namespace : String,
- method : String
- )
- return {} of String => String unless (
- API[namespace]? &&
- API[namespace][method] &&
- API[namespace][method][:args]?
- )
-
- # get method args
- args = API[namespace][method][:args] as \
- Hash(String, Hash(Symbol, String | Symbol | Bool))
-
- args.keys.reduce({} of String => String) do |r, arg_name|
- arg_data = args[arg_name] as Hash(Symbol, String|Symbol|Bool)
-
- # check for required parameter
- if arg_data[:required] && !params.has_key?(arg_name)
- raise "missing required parameter: %s" % [arg_name]
- end
-
- # get value
- val = if arg_data[:required]
- params.fetch(arg_name)
- # elsif arg_data.has_key?(:default)
- elsif arg_data[:default]?
- params.fetch(arg_name, arg_data[:default] as String)
- else
- nil
- end
-
- if val
- # check value
- if !TYPE_CHECKS[arg_data[:type]].match(val)
- raise "invalid parameter format: %s" % [arg_name]
- end
-
- # add value to result
- r[arg_name] = val
- end
-
- # return result
- r
- end
- end
-
- ################
- # post methods #
- ################
-
- NO_TAGS = [] of Array(String)
-
- private def do_post_get_posts(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- @models.post.get_posts(
- site_id: get_site(context),
- q: args["q"]? || "",
- tags: get_posts_tags(args["tags"]),
- page: args.has_key?("page") ? args["page"].to_i : 1,
- ).to_json
- end
-
- private def do_post_add_post(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- post_id = @models.post.add_post(
- site_id: get_site(context),
- slug: args["slug"],
- name: args["name"],
- body: args["body"],
- tags: get_tags(args["tags"]?),
- )
-
- # return json
- { post_id: post_id }.to_json
- end
-
- private def do_post_update_post(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- post_id = @models.post.update_post(
- site_id: get_site(context),
- post_id: args["post_id"].to_i,
- slug: args["slug"]?,
- name: args["name"],
- body: args["body"],
- tags: args.has_key?("tags") ? get_tags(args["tags"]?) : nil,
- posted: args["posted"]?,
- )
-
- # return json
- { ok: true }.to_json
- end
-
- private def do_post_remove_posts(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- @models.post.remove_posts(
- site_id: get_site(context),
- post_ids: args["post_ids"].split(',').map { |post_id|
- post_id.to_s.to_i
- },
- )
-
- { ok: true }.to_json
- end
-
- private def do_post_set_tags(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- @models.post.set_tags(
- site_id: get_site(context),
- post_id: (args["post_id"] as String).to_i,
- tags: get_tags(args["tags"]?),
- )
-
- { ok: true}.to_json
- end
-
- ###############
- # dir methods #
- ###############
-
- private def do_dir_add(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- private def do_dir_remove(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- ################
- # file methods #
- ################
-
- private def do_file_add(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- private def do_file_remove(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- ###############
- # tag methods #
- ###############
-
- private def do_tag_get_tags(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- [{foo: "bar"}, {foo: "asdf"}].to_json
- end
-
- private def do_tag_remove_tags(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- true.to_json
- end
-
- ################
- # site methods #
- ################
-
- private def do_site_add_site(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- private def do_site_remove_sites(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- private def do_site_set_default(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- private def do_site_set_domains(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- # TODO
- {ok: true}.to_json
- end
-
- ################
- # test methods #
- ################
-
- private def do_test_version(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- {version: Guff::VERSION}.to_json
- end
-
- private def do_test_get_posts(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- [{foo: "bar"}, {foo: "asdf"}].to_json
- end
-
- private def do_test_error(
- context : HTTP::Server::Context,
- args : Hash(String, String)
- )
- raise "some random error"
- end
-
- ###################
- # utility methods #
- ###################
-
- private def get_site(context : HTTP::Server::Context) : Int?
- @models.site.to_site(context.request.headers["host"]?)
- end
-
- private def get_tags(
- s : String?
- ) : Array(String)
- if s && s.size > 0
- Array(String).from_json(s)
- else
- [] of String
- end
- end
-
- private def get_posts_tags(
- s : String?
- ) : Array(Array(String))
- if s && s.size > 0
- Array(Array(String)).from_json(s)
- else
- [] of Array(String)
- end
- end
- end
-end
diff --git a/src/guff/api/content-type.cr b/src/guff/api/content-type.cr
new file mode 100644
index 0000000..589e6b0
--- /dev/null
+++ b/src/guff/api/content-type.cr
@@ -0,0 +1,10 @@
+module Guff::API::ContentType
+ CONTENT_TYPES = {
+ "development": "text/html; charset=utf-8",
+ "production": "application/json; charset=utf8",
+ }
+
+ private def get_content_type
+ CONTENT_TYPES[@models.config["environment"]]
+ end
+end
diff --git a/src/guff/api/dir.cr b/src/guff/api/dir.cr
new file mode 100644
index 0000000..9b3779e
--- /dev/null
+++ b/src/guff/api/dir.cr
@@ -0,0 +1,23 @@
+require "json"
+
+module Guff
+ module API
+ module DirAPI
+ private def do_dir_add(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+
+ private def do_dir_remove(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+ end
+ end
+end
diff --git a/src/guff/api/file.cr b/src/guff/api/file.cr
new file mode 100644
index 0000000..353de9d
--- /dev/null
+++ b/src/guff/api/file.cr
@@ -0,0 +1,23 @@
+require "json"
+
+module Guff
+ module API
+ module FileAPI
+ private def do_file_add(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+
+ private def do_file_remove(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+ end
+ end
+end
diff --git a/src/guff/api/methods.cr b/src/guff/api/methods.cr
new file mode 100644
index 0000000..758cca9
--- /dev/null
+++ b/src/guff/api/methods.cr
@@ -0,0 +1,366 @@
+require "json"
+
+module Guff
+ module API
+ module Methods
+ METHODS = {
+ "post": {
+ "get_posts": {
+ text: "Get posts matching query.",
+
+ args: {
+ "q": {
+ text: "Search string.",
+ type: :text,
+ required: false,
+ default: "",
+ },
+
+ "page": {
+ text: "Page number",
+ type: :int,
+ required: false,
+ default: "1",
+ },
+
+ "tags": {
+ text: "Comma-separated list of tags (union)",
+ # type: :tag_list,
+ type: :json,
+ required: false,
+ default: "",
+ },
+
+ "sort": {
+ text: "Sort order of results",
+ type: :sort,
+ required: false,
+ default: "date,desc",
+ },
+
+ "cols": {
+ text: "Column list",
+ type: :text,
+ required: false,
+ },
+ },
+ },
+
+ "add_post": {
+ text: "Create new post.",
+ args: {
+ "name": {
+ text: "Post title.",
+ type: :text,
+ required: true,
+ },
+
+ "slug": {
+ text: "Post slug.",
+ type: :slug,
+ required: true,
+ },
+
+ "body": {
+ text: "Post body.",
+ type: :text,
+ required: true,
+ },
+
+ "tags": {
+ text: "Post tags.",
+ # type: :tag_list,
+ type: :json,
+ required: false,
+ default: "",
+ },
+ },
+ },
+
+ "update_post": {
+ text: "Update existing post.",
+ args: {
+ "name": {
+ text: "Post title.",
+ type: :text,
+ required: false,
+ },
+
+ "slug": {
+ text: "Post slug.",
+ type: :slug,
+ required: false,
+ },
+
+ "body": {
+ text: "Post body.",
+ type: :text,
+ required: false,
+ },
+
+ "tags": {
+ text: "Post tags.",
+ type: :json,
+ required: false,
+ },
+
+ "posted": {
+ text: "Is this post posted?",
+ type: :bool,
+ required: false,
+ },
+ },
+ },
+
+ "remove_posts": {
+ text: "Remove existing posts.",
+
+ args: {
+ "post_ids": {
+ text: "Post IDs.",
+ type: :int_list,
+ required: false,
+ default: "",
+ },
+
+ "tags": {
+ text: "Post tags.",
+ # type: :tag_list,
+ type: :json,
+ required: false,
+ default: "",
+ },
+ },
+ },
+
+ "set_tags": {
+ text: "Set tags for posts.",
+
+ args: {
+ "post_ids": {
+ text: "Post IDs.",
+ type: :int_list,
+ required: true,
+ },
+ },
+ },
+ },
+
+ "dir": {
+ "add": {
+ text: "Create new directory",
+
+ args: {
+ "path": {
+ text: "Path to new directory",
+ type: :path,
+ required: true,
+ },
+ },
+ },
+
+ "remove": {
+ text: "Remove directory",
+
+ args: {
+ "path": {
+ text: "Path to existing directory",
+ type: :path,
+ required: true,
+ },
+ },
+ },
+ },
+
+ "file": {
+ "add": {
+ text: "Upload new file",
+
+ args: {
+ "path": {
+ text: "Destination file path",
+ type: :path,
+ required: true,
+ },
+ },
+ },
+
+ "remove": {
+ text: "Remove existing file",
+
+ args: {
+ "path": {
+ text: "Destination file path",
+ type: :path,
+ required: true,
+ },
+ },
+ },
+ },
+
+ "tag": {
+ "get_tags": {
+ text: "Get list of tags",
+ },
+
+ "remove_tags": {
+ text: "Remove set of tags",
+
+ args: {
+ "tags": {
+ text: "Tags to remove.",
+ # type: :tag_list,
+ type: :json,
+ required: false,
+ default: "",
+ },
+ },
+ },
+ },
+
+ "site": {
+ "add_site": {
+ text: "Create a new site",
+
+ args: {
+ "name": {
+ text: "Name of this site",
+ type: :text,
+ required: true,
+ },
+
+ "default": {
+ text: "Is this the default site?",
+ type: :bool,
+ required: false,
+ default: "f",
+ },
+
+ "domains": {
+ text: "Array of domain matches.",
+ type: :domain_list,
+ required: true,
+ },
+ },
+ },
+
+ "remove_sites": {
+ text: "Remove one or more existing sites",
+
+ args: {
+ "site_ids": {
+ text: "IDs of sites to remove",
+ type: :int_list,
+ required: true,
+ },
+ },
+ },
+
+ "set_default": {
+ text: "Set new default site",
+
+ args: {
+ "site_id": {
+ text: "ID of new default site",
+ type: :int,
+ required: true,
+ },
+ },
+ },
+
+ "set_domains": {
+ text: "Set domains for given site",
+
+ args: {
+ "site_id": {
+ text: "ID of site",
+ type: :int,
+ required: true,
+ },
+
+ "domains": {
+ text: "Domains for this site.",
+ type: :domain_list,
+ required: true,
+ },
+ },
+ },
+ },
+
+ "test": {
+ "version": {
+ text: "Get version",
+ },
+
+ "get_posts": {
+ text: "Test get posts",
+ },
+
+ "error": {
+ text: "Test error response",
+ },
+ }
+ }
+
+ TYPE_CHECKS = {
+ text: /.*/,
+ slug: /^[a-z0-9\.-]+$/,
+ int: /^\d+$/,
+ int_list: /^\d+(?:,\d+)*$/,
+ sort: /^[a-z0-9_]+,(?:asc|desc)$/,
+
+ # FIXME: lock these down more
+ json: /.*/,
+ path: /^[^\/].*[^\/]$/,
+ }
+
+ private def get_method_args(
+ params : HTTP::Params,
+ namespace : String,
+ method : String
+ )
+ return {} of String => String unless (
+ METHODS[namespace]? &&
+ METHODS[namespace][method] &&
+ METHODS[namespace][method][:args]?
+ )
+
+ # get method args
+ args = METHODS[namespace][method][:args] as \
+ Hash(String, Hash(Symbol, String | Symbol | Bool))
+
+ args.keys.reduce({} of String => String) do |r, arg_name|
+ arg_data = args[arg_name] as Hash(Symbol, String|Symbol|Bool)
+
+ # check for required parameter
+ if arg_data[:required] && !params.has_key?(arg_name)
+ raise "missing required parameter: %s" % [arg_name]
+ end
+
+ # get value
+ val = if arg_data[:required]
+ params.fetch(arg_name)
+ # elsif arg_data.has_key?(:default)
+ elsif arg_data[:default]?
+ params.fetch(arg_name, arg_data[:default] as String)
+ else
+ nil
+ end
+
+ if val
+ # check value
+ if !TYPE_CHECKS[arg_data[:type]].match(val)
+ raise "invalid parameter format: %s" % [arg_name]
+ end
+
+ # add value to result
+ r[arg_name] = val
+ end
+
+ # return result
+ r
+ end
+ end
+ end
+ end
+end
diff --git a/src/guff/api/post.cr b/src/guff/api/post.cr
new file mode 100644
index 0000000..0bc6377
--- /dev/null
+++ b/src/guff/api/post.cr
@@ -0,0 +1,80 @@
+require "json"
+
+module Guff
+ module API
+ module PostAPI
+ private def do_post_get_posts(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ @models.post.get_posts(
+ site_id: get_site(context),
+ q: args["q"]? || "",
+ tags: get_posts_tags(args["tags"]),
+ page: args.has_key?("page") ? args["page"].to_i : 1,
+ ).to_json
+ end
+
+ private def do_post_add_post(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ post_id = @models.post.add_post(
+ site_id: get_site(context),
+ slug: args["slug"],
+ name: args["name"],
+ body: args["body"],
+ tags: get_tags(args["tags"]?),
+ )
+
+ # return json
+ { post_id: post_id }.to_json
+ end
+
+ private def do_post_update_post(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ post_id = @models.post.update_post(
+ site_id: get_site(context),
+ post_id: args["post_id"].to_i,
+ slug: args["slug"]?,
+ name: args["name"],
+ body: args["body"],
+ tags: args.has_key?("tags") ? get_tags(args["tags"]?) : nil,
+ posted: args["posted"]?,
+ )
+
+ # return json
+ { ok: true }.to_json
+ end
+
+ private def do_post_remove_posts(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ @models.post.remove_posts(
+ site_id: get_site(context),
+ post_ids: args["post_ids"].split(',').map { |post_id|
+ post_id.to_s.to_i
+ },
+ )
+
+ { ok: true }.to_json
+ end
+
+ private def do_post_set_tags(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ @models.post.set_tags(
+ site_id: get_site(context),
+ post_id: (args["post_id"] as String).to_i,
+ tags: get_tags(args["tags"]?),
+ )
+
+ { ok: true}.to_json
+ end
+ end
+ end
+end
diff --git a/src/guff/api/site.cr b/src/guff/api/site.cr
new file mode 100644
index 0000000..b99f550
--- /dev/null
+++ b/src/guff/api/site.cr
@@ -0,0 +1,39 @@
+require "json"
+
+module Guff
+ module API
+ module SiteAPI
+ private def do_site_add_site(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+
+ private def do_site_remove_sites(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+
+ private def do_site_set_default(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+
+ private def do_site_set_domains(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ # TODO
+ {ok: true}.to_json
+ end
+ end
+ end
+end
diff --git a/src/guff/api/tag.cr b/src/guff/api/tag.cr
new file mode 100644
index 0000000..e91fb60
--- /dev/null
+++ b/src/guff/api/tag.cr
@@ -0,0 +1,21 @@
+require "json"
+
+module Guff
+ module API
+ module TagAPI
+ private def do_tag_get_tags(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ [{foo: "bar"}, {foo: "asdf"}].to_json
+ end
+
+ private def do_tag_remove_tags(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ true.to_json
+ end
+ end
+ end
+end
diff --git a/src/guff/api/test.cr b/src/guff/api/test.cr
new file mode 100644
index 0000000..7549c11
--- /dev/null
+++ b/src/guff/api/test.cr
@@ -0,0 +1,28 @@
+require "json"
+
+module Guff
+ module API
+ module TestAPI
+ private def do_test_version(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ {version: Guff::VERSION}.to_json
+ end
+
+ private def do_test_get_posts(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ [{foo: "bar"}, {foo: "asdf"}].to_json
+ end
+
+ private def do_test_error(
+ context : HTTP::Server::Context,
+ args : Hash(String, String)
+ )
+ raise "some random error"
+ end
+ end
+ end
+end
diff --git a/src/guff/api/util.cr b/src/guff/api/util.cr
new file mode 100644
index 0000000..525f098
--- /dev/null
+++ b/src/guff/api/util.cr
@@ -0,0 +1,31 @@
+require "json"
+
+module Guff
+ module API
+ module Util
+ private def get_site(context : HTTP::Server::Context) : Int?
+ @models.site.to_site(context.request.headers["host"]?)
+ end
+
+ private def get_tags(
+ s : String?
+ ) : Array(String)
+ if s && s.size > 0
+ Array(String).from_json(s)
+ else
+ [] of String
+ end
+ end
+
+ private def get_posts_tags(
+ s : String?
+ ) : Array(Array(String))
+ if s && s.size > 0
+ Array(Array(String)).from_json(s)
+ else
+ [] of Array(String)
+ end
+ end
+ end
+ end
+end