kvs.erl 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. -module(kvs).
  2. -author('Maxim Sokhatsky <maxim@synrc.com>').
  3. -include_lib("kvs/include/users.hrl").
  4. -include_lib("kvs/include/translations.hrl").
  5. -include_lib("kvs/include/groups.hrl").
  6. -include_lib("kvs/include/feeds.hrl").
  7. -include_lib("kvs/include/acls.hrl").
  8. -include_lib("kvs/include/meetings.hrl").
  9. -include_lib("kvs/include/invites.hrl").
  10. -include_lib("kvs/include/config.hrl").
  11. -include_lib("kvs/include/accounts.hrl").
  12. -include_lib("kvs/include/log.hrl").
  13. -include_lib("kvs/include/membership.hrl").
  14. -include_lib("kvs/include/payments.hrl").
  15. -include_lib("stdlib/include/qlc.hrl").
  16. -include_lib("kvs/include/feed_state.hrl").
  17. -compile(export_all).
  18. -define(DBA, store_riak).
  19. start() -> DBA = ?DBA, DBA:start().
  20. dir() -> DBA = ?DBA, DBA:dir().
  21. purchases(UserId) -> DBA = ?DBA, DBA:purchases(UserId).
  22. transactions(UserId) -> DBA = ?DBA, DBA:transactions(UserId).
  23. stop() -> DBA = ?DBA, DBA:stop().
  24. initialize() -> DBA = ?DBA, DBA:initialize().
  25. delete() -> DBA = ?DBA, DBA:delete().
  26. init_indexes() -> DBA = ?DBA, DBA:init_indexes().
  27. traversal( _, _, undefined, _) -> [];
  28. traversal(_, _, _, 0) -> [];
  29. traversal(RecordType, PrevPos, Next, Count)->
  30. case kvs:get(RecordType, Next) of
  31. {error,_} -> [];
  32. {ok, R} ->
  33. Prev = element(PrevPos, R),
  34. Count1 = case Count of C when is_integer(C) -> C - 1; _-> Count end,
  35. [R | traversal(RecordType, PrevPos, Prev, Count1)]
  36. end.
  37. init_db() ->
  38. case kvs:get(user,"alice") of
  39. {error,_} ->
  40. % DBA = ?DBA,
  41. % DBA:init_db(),
  42. % kvs_membership:add_sample_data(),
  43. add_seq_ids(),
  44. kvs_account:create_account(system),
  45. add_sample_users(),
  46. add_sample_packages(),
  47. add_translations();
  48. % case is_production() of
  49. % false ->
  50. % add_purchases();
  51. % true ->
  52. % do_nothing
  53. % end;
  54. {ok,_} -> ignore
  55. end.
  56. is_production() ->
  57. case kvs:get(config, "debug/production", false) of
  58. {ok, true} -> true;
  59. _ -> false
  60. end.
  61. add_purchases() ->
  62. {ok, Pkg1} = kvs_membership:get_package(1),
  63. {ok, Pkg2} = kvs_membership:get_package(2),
  64. {ok, Pkg3} = kvs_membership:get_package(3),
  65. {ok, Pkg4} = kvs_membership:get_package(4),
  66. PList = [{"doxtop", Pkg1},{"maxim", Pkg2},{"maxim",Pkg4}, {"kate", Pkg3} ],
  67. [ok = add_purchase(U, P) || {U, P} <- PList],
  68. ok.
  69. add_purchase(UserId, Package) ->
  70. {ok, MPId} = kvs_payment:add_payment(#payment{user_id=UserId, membership=Package}),
  71. kvs_payment:set_payment_state(MPId, ?MP_STATE_DONE, undefined).
  72. add_seq_ids() ->
  73. Init = fun(Key) ->
  74. case kvs:get(id_seq, Key) of
  75. {error, _} -> ok = kvs:put(#id_seq{thing = Key, id = 0});
  76. {ok, _} -> ignore
  77. end
  78. end,
  79. Init("meeting"),
  80. Init("user_transaction"),
  81. Init("transaction"),
  82. Init("membership"),
  83. Init("payment"),
  84. Init("acl"),
  85. Init("acl_entry"),
  86. Init("feed"),
  87. Init("entry"),
  88. Init("like_entry"),
  89. Init("likes"),
  90. Init("one_like"),
  91. Init("comment"),
  92. Init("save_table").
  93. add_translations() ->
  94. lists:foreach(fun({English, Lang, Word}) ->
  95. ok = kvs:put(#translation{english = English, lang = "en", word = English}),
  96. ok = kvs:put(#translation{english = English, lang = Lang, word = Word}),
  97. ok
  98. end, ?URL_DICTIONARY).
  99. add_sample_users() ->
  100. Groups = [ #group{username="Clojure"},
  101. #group{username="Haskell"},
  102. #group{username="Erlang"} ],
  103. UserList = [
  104. #user{username = "maxim", password="kaka15ra",
  105. name = "Maxim", surname = "Sokhatsky", feed = feed_create(),
  106. type = admin, direct = feed_create(),
  107. sex=m,
  108. status=ok,
  109. team = create_team("tours"),
  110. email="maxim.sokhatsky@gmail.com"},
  111. #user{username = "doxtop", password="password",
  112. feed = feed_create(),
  113. name = "Andrii Zadorozhnii",
  114. email="doxtop@synrc.com",
  115. type=admin,
  116. team = create_team("tours"), direct = feed_create(),
  117. status=ok,
  118. age={1981,9,29},
  119. register_date={1345,14071,852889}
  120. }
  121. ],
  122. kvs:put(Groups),
  123. [ begin
  124. kvs_account:create_account(Me#user.username),
  125. kvs_account:transaction(Me#user.username, quota, kvs:get_config("accounts/default_quota", 300), #tx_default_assignment{}),
  126. kvs:put(Me#user{password = kvs:sha(Me#user.password), starred = feed_create(), pinned = feed_create()})
  127. end || Me <- UserList],
  128. kvs_acl:define_access({user, "maxim"}, {feature, admin}, allow),
  129. kvs_acl:define_access({user_type, admin}, {feature, admin}, allow),
  130. [ kvs_user:subscribe(Me#user.username, Her#user.username) || Her <- UserList, Me <- UserList, Her /= Me ],
  131. [ kvs_user:init_mq(U) || U <- UserList ],
  132. ok.
  133. add_sample_packages() -> kvs_membership:add_sample_data().
  134. version() -> ?INFO("version: ~p", [1]).
  135. % blocking
  136. block_user(Who, Whom) -> DBA=?DBA, DBA:block_user(Who, Whom).
  137. list_blocks(Who) -> DBA=?DBA, DBA:list_blocks(Who).
  138. unblock_user(Who, Whom) -> DBA=?DBA, DBA:unblock_user(Who, Whom).
  139. list_blocked_me(Me) -> DBA=?DBA, DBA:list_blocked_me(Me).
  140. is_user_blocked(Who, Whom) -> DBA=?DBA, DBA:is_user_blocked(Who, Whom).
  141. % configs
  142. add_configs() ->
  143. %% smtp
  144. kvs:put(#config{key="smtp/user", value="noreply@synrc.com"}),
  145. kvs:put(#config{key="smtp/password", value="maxim@synrc.com"}),
  146. kvs:put(#config{key="smtp/host", value="mail.synrc.com"}),
  147. kvs:put(#config{key="smtp/port", value=465}),
  148. kvs:put(#config{key="smtp/with_ssl", value=true}),
  149. kvs:put(#config{key="accounts/default_quota", value=2000}),
  150. kvs:put(#config{key="accounts/quota_limit/soft", value=-30}),
  151. kvs:put(#config{key="accounts/quota_limit/hard", value=-100}),
  152. kvs:put(#config{key="purchase/notifications/email", value=["maxim@synrc.com"]}),
  153. kvs:put(#config{key="delivery/notifications/email", value=["maxim@synrc.com"]}).
  154. put(Record) ->
  155. DBA=?DBA,
  156. DBA:put(Record).
  157. put_if_none_match(Record) ->
  158. DBA=?DBA,
  159. DBA:put_if_none_match(Record).
  160. update(Record, Meta) ->
  161. DBA=?DBA,
  162. DBA:update(Record, Meta).
  163. get(RecordName, Key) ->
  164. DBA=?DBA,
  165. DBA:get(RecordName, Key).
  166. get_for_update(RecordName, Key) ->
  167. DBA=?DBA,
  168. DBA:get_for_update(RecordName, Key).
  169. get(RecordName, Key, Default) ->
  170. DBA=?DBA,
  171. case DBA:get(RecordName, Key) of
  172. {ok,{RecordName,Key,Value}} ->
  173. ?INFO("db:get config value ~p,", [{RecordName, Key, Value}]),
  174. {ok,Value};
  175. {error, _B} ->
  176. ?INFO("db:get new config value ~p,", [{RecordName, Key, Default}]),
  177. DBA:put({RecordName,Key,Default}),
  178. {ok,Default} end.
  179. get_config(Key, Default) -> {ok, Value} = get(config, Key, Default), Value.
  180. get_word(Word) -> get(ut_word,Word).
  181. get_translation({Lang,Word}) -> DBA=?DBA, DBA:get_translation({Lang,Word}).
  182. % delete
  183. delete(Keys) -> DBA=?DBA, DBA:delete(Keys).
  184. delete(Tab, Key) -> ?INFO("db:delete ~p:~p",[Tab, Key]), DBA=?DBA,DBA:delete(Tab, Key).
  185. delete_by_index(Tab, IndexId, IndexVal) -> DBA=?DBA,DBA:delete_by_index(Tab, IndexId, IndexVal).
  186. % select
  187. multi_select(RecordName, Keys) -> DBA=?DBA,DBA:multi_select(RecordName, Keys).
  188. select(From, PredicateFunction) -> ?INFO("db:select ~p, ~p",[From,PredicateFunction]), DBA=?DBA, DBA:select(From, PredicateFunction).
  189. count(RecordName) -> DBA=?DBA,DBA:count(RecordName).
  190. all(RecordName) -> DBA=?DBA,DBA:all(RecordName).
  191. all_by_index(RecordName, Index, IndexValue) -> DBA=?DBA,DBA:all_by_index(RecordName, Index, IndexValue).
  192. % id generator
  193. next_id(RecordName) -> DBA=?DBA,DBA:next_id(RecordName).
  194. next_id(RecordName, Incr) -> DBA=?DBA,DBA:next_id(RecordName, Incr).
  195. next_id(RecordName, Default, Incr) -> DBA=?DBA,DBA:next_id(RecordName, Default, Incr).
  196. % browser counter
  197. delete_browser_counter_older_than(MinTS) -> DBA=?DBA,DBA:delete_browser_counter_older_than(MinTS).
  198. browser_counter_by_game(Game) -> DBA=?DBA,DBA:browser_counter_by_game(Game).
  199. % invites
  200. unused_invites() -> DBA=?DBA,DBA:unused_invites().
  201. user_by_verification_code(Code) -> DBA=?DBA,DBA:user_by_verification_code(Code).
  202. user_by_facebook_id(FBId) -> DBA=?DBA,DBA:user_by_facebook_id(FBId).
  203. user_by_email(Email) -> DBA=?DBA,DBA:user_by_email(Email).
  204. user_by_username(Name) -> DBA=?DBA,DBA:user_by_username(Name).
  205. add_invite_to_issuer(User, O) -> DBA=?DBA,DBA:add_invite_to_issuer(User, O).
  206. invite_code_by_issuer(User) -> DBA=?DBA,DBA:invite_code_by_issuer(User).
  207. invite_code_by_user(User) -> DBA=?DBA,DBA:invite_code_by_user(User).
  208. % game info
  209. get_save_tables(Id) -> DBA=?DBA,DBA:get_save_tables(Id).
  210. save_game_table_by_id(Id) -> DBA=?DBA,DBA:save_game_table_by_id(Id).
  211. % feeds
  212. feed_add_direct_message(FId, User, To, EntryId, Desc, Medias) -> DBA=?DBA,DBA:feed_add_direct_message(FId, User, To, EntryId, Desc, Medias).
  213. feed_add_entry(FId, User, EntryId, Desc, Medias) -> DBA=?DBA,DBA:feed_add_entry(FId, User, EntryId, Desc, Medias).
  214. feed_add_entry(FId, User, To, EntryId, Desc, Medias, Type, SharedBy) -> DBA=?DBA,DBA:feed_add_entry(FId, User, To, EntryId, Desc, Medias, Type, SharedBy).
  215. acl_add_entry(AclId, Accessor, Action) -> DBA=?DBA,DBA:acl_add_entry(AclId, Accessor, Action).
  216. acl_entries(AclId) -> DBA=?DBA,DBA:acl_entries(AclId).
  217. entry_by_id(EntryId) -> DBA=?DBA,DBA:entry_by_id(EntryId).
  218. comment_by_id(CommentId) -> DBA=?DBA,DBA:comment_by_id(CommentId).
  219. comments_by_entry({_EId, _FId} = EntryId) -> DBA=?DBA,DBA:comments_by_entry(EntryId).
  220. entries_in_feed(FeedId) -> DBA=?DBA,DBA:entries_in_feed(FeedId, undefined, all).
  221. entries_in_feed(FeedId, Count) -> DBA=?DBA,DBA:entries_in_feed(FeedId, undefined, Count).
  222. entries_in_feed(FeedId, StartFrom, Count) -> DBA=?DBA, DBA:entries_in_feed(FeedId, StartFrom, Count).
  223. add_comment(FId, User, EntryId, ParentComment, CommentId, Content, Medias) -> DBA=?DBA, DBA:feed_add_comment(FId, User, EntryId, ParentComment, CommentId, Content, Medias).
  224. feed_direct_messages(FId, StartFrom, Count) -> DBA=?DBA, DBA:entries_in_feed(FId, StartFrom, Count).
  225. % tournaments
  226. tournament_waiting_queue(TID) -> DBA=?DBA, DBA:tournament_waiting_queue(TID).
  227. join_tournament(UID,TID) -> DBA=?DBA, DBA:join_tournament(UID,TID).
  228. leave_tournament(UID,TID) -> DBA=?DBA, DBA:leave_tournament(UID,TID).
  229. tournament_pop_waiting_player(TID) -> DBA=?DBA, DBA:tournament_pop_waiting_player(TID).
  230. user_tournaments(UID) -> DBA=?DBA, DBA:user_tournaments(UID).
  231. add_transaction_to_user(User, Tx) -> DBA=?DBA, DBA:add_transaction_to_user(User, Tx).
  232. get_purchases_by_user(User, Count, States) -> DBA=?DBA, DBA:get_purchases_by_user(User, Count, States).
  233. get_purchases_by_user(User, StartFromPurchase, Count, States) -> DBA=?DBA, DBA:get_purchases_by_user(User, StartFromPurchase, Count, States).
  234. make_admin(User) ->
  235. {ok,U} = kvs:get(user, User),
  236. kvs:put(U#user{type = admin}),
  237. kvs_acl:define_access({user, U#user.username}, {feature, admin}, allow),
  238. kvs_acl:define_access({user_type, admin}, {feature, admin}, allow),
  239. ok.
  240. make_rich(User) ->
  241. Q = kvs:get_config("accounts/default_quota", 300),
  242. kvs_account:transaction(User, quota, Q * 100, #tx_default_assignment{}),
  243. kvs_account:transaction(User, internal, Q, #tx_default_assignment{}),
  244. kvs_account:transaction(User, currency, Q * 2, #tx_default_assignment{}).
  245. feed_create() ->
  246. FId = kvs:next_id("feed", 1),
  247. ok = kvs:put(#feed{id = FId} ),
  248. FId.
  249. create_team(Name) ->
  250. TID = kvs:next_id("team",1),
  251. ok = kvs:put(Team = #team{id=TID,name=Name}),
  252. TID.
  253. list_to_term(String) ->
  254. {ok, T, _} = erl_scan:string(String++"."),
  255. case erl_parse:parse_term(T) of
  256. {ok, Term} ->
  257. Term;
  258. {error, Error} ->
  259. Error
  260. end.
  261. save_db(Path) ->
  262. Data = lists:append([all(B) || B <- [list_to_term(B) || B <- store_riak:dir()] ]),
  263. kvs:save(Path, Data).
  264. load_db(Path) ->
  265. add_seq_ids(),
  266. AllEntries = kvs:load(Path),
  267. [{_,_,{_,Handler}}] = ets:lookup(config, "riak_client"),
  268. [case is_tuple(E) of
  269. false -> skip;
  270. true -> put(E)
  271. end || E <- AllEntries].
  272. make_paid_fake(UId) ->
  273. put({user_purchase, UId, "fake_purchase"}).
  274. save(Key, Value) ->
  275. Dir = ling:trim_from_last(Key, "/"),
  276. filelib:ensure_dir(Dir),
  277. file:write_file(Key, term_to_binary(Value)).
  278. load(Key) ->
  279. {ok, Bin} = file:read_file(Key),
  280. binary_to_term(Bin).
  281. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
  282. handle_notice(["kvs", "group", Owner, "put"] = Route,
  283. Message, #state{owner = Owner, type =Type} = State) ->
  284. ?INFO("queue_action(~p): group put: Owner=~p, Route=~p, Message=~p", [self(), {Type, Owner}, Route, Message]),
  285. kvs:put(Message),
  286. {noreply, State};
  287. handle_notice(["kvs", "user", Owner, "put"] = Route,
  288. Message, #state{owner = Owner, type =Type} = State) ->
  289. ?INFO("queue_action(~p): user put: Owner=~p, Route=~p, Message=~p", [self(), {Type, Owner}, Route, Message]),
  290. kvs:put(Message),
  291. {noreply, State};
  292. handle_notice(["kvs","system", "put"] = Route,
  293. Message, #state{owner = Owner, type =Type} = State) ->
  294. ?INFO("queue_action(~p): system put: Owner=~p, Route=~p, Message=~p", [self(), {Type, Owner}, Route, Message]),
  295. kvs:put(Message),
  296. {noreply, State};
  297. handle_notice(["kvs","system", "delete"] = Route,
  298. Message, #state{owner = Owner, type =Type} = State) ->
  299. ?INFO("queue_action(~p): system delete: Owner=~p, Route=~p, Message=~p", [self(), {Type, Owner}, Route, Message]),
  300. {Where, What} = Message,
  301. kvs:delete(Where, What),
  302. {noreply, State};
  303. handle_notice(Route, Message, State) -> error_logger:info_msg("Unknown KVS notice").
  304. coalesce(undefined, B) -> B;
  305. coalesce(A, _) -> A.
  306. sha(Raw) ->
  307. lists:flatten(
  308. [io_lib:format("~2.16.0b", [N]) || <<N>> <= crypto:sha(Raw)]).
  309. sha_upper(Raw) ->
  310. SHA = sha(Raw),
  311. string:to_upper(SHA).