kvs.erl 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  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_packages.hrl").
  14. -include_lib("stdlib/include/qlc.hrl").
  15. -include_lib("kvs/include/feed_state.hrl").
  16. -compile(export_all).
  17. -define(DBA, store_riak).
  18. start() -> DBA = ?DBA, DBA:start().
  19. dir() -> DBA = ?DBA, DBA:dir().
  20. purchases(UserId) -> DBA = ?DBA, DBA:purchases(UserId).
  21. transactions(UserId) -> DBA = ?DBA, DBA:transactions(UserId).
  22. stop() -> DBA = ?DBA, DBA:stop().
  23. initialize() -> DBA = ?DBA, DBA:initialize().
  24. delete() -> DBA = ?DBA, DBA:delete().
  25. init_indexes() -> DBA = ?DBA, DBA:init_indexes().
  26. init_db() ->
  27. case kvs:get(user,"alice") of
  28. {error,_} ->
  29. DBA = ?DBA,
  30. DBA:init_db(),
  31. add_seq_ids(),
  32. accounts:create_account(system),
  33. add_sample_users(),
  34. add_sample_packages(),
  35. add_translations(),
  36. case is_production() of
  37. false ->
  38. add_purchases();
  39. true ->
  40. do_nothing
  41. end;
  42. {ok,_} -> ignore
  43. end.
  44. is_production() ->
  45. case kvs:get(config, "debug/production", false) of
  46. {ok, true} -> true;
  47. _ -> false
  48. end.
  49. add_purchases() ->
  50. {ok, Pkg1} = membership_packages:get_package(1),
  51. {ok, Pkg2} = membership_packages:get_package(2),
  52. {ok, Pkg3} = membership_packages:get_package(3),
  53. {ok, Pkg4} = membership_packages:get_package(4),
  54. PList = [{"doxtop", Pkg1},{"maxim", Pkg2},{"maxim",Pkg4}, {"kate", Pkg3} ],
  55. [ok = add_purchase(U, P) || {U, P} <- PList],
  56. ok.
  57. add_purchase(UserId, Package) ->
  58. {ok, MPId} = membership_packages:add_purchase(
  59. #membership_purchase{user_id=UserId, membership_package=Package }),
  60. membership_packages:set_purchase_state(MPId, ?MP_STATE_DONE, undefined).
  61. add_seq_ids() ->
  62. Init = fun(Key) ->
  63. case kvs:get(id_seq, Key) of
  64. {error, _} -> ok = kvs:put(#id_seq{thing = Key, id = 0});
  65. {ok, _} -> ignore
  66. end
  67. end,
  68. Init("meeting"),
  69. Init("user_transaction"),
  70. Init("transaction"),
  71. Init("membership_purchase"),
  72. Init("acl"),
  73. Init("acl_entry"),
  74. Init("feed"),
  75. Init("entry"),
  76. Init("like_entry"),
  77. Init("likes"),
  78. Init("one_like"),
  79. Init("comment"),
  80. Init("save_table").
  81. add_translations() ->
  82. lists:foreach(fun({English, Lang, Word}) ->
  83. ok = kvs:put(#translation{english = English, lang = "en", word = English}),
  84. ok = kvs:put(#translation{english = English, lang = Lang, word = Word}),
  85. ok
  86. end, ?URL_DICTIONARY).
  87. add_sample_users() ->
  88. UserList = [
  89. #user{username = "maxim", password="kaka15ra",
  90. name = "Maxim", surname = "Sokhatsky", feed = feed_create(),
  91. type = admin, direct = feed_create(),
  92. sex=m,
  93. status=ok,
  94. team = create_team("tours"),
  95. email="maxim.sokhatsky@gmail.com"},
  96. #user{username = "doxtop", password="password",
  97. feed = feed_create(),
  98. name = "Andrii Zadorozhnii",
  99. email="doxtop@synrc.com",
  100. type=admin,
  101. team = create_team("tours"), direct = feed_create(),
  102. status=ok,
  103. age={1981,9,29},
  104. register_date={1345,14071,852889}
  105. }
  106. ],
  107. ?INFO("creating groups"),
  108. GId1 = groups:create_group_directly_to_db("maxim", "kakaranet", "Kakaranet", "Kakaranet'e Hoşgeldiniz", public),
  109. GId2 = groups:create_group_directly_to_db("maxim", "yeniler", "Yeniler", "So, you must be new here.", public),
  110. ?INFO("adding users accounts"),
  111. [ begin
  112. accounts:create_account(Me#user.username),
  113. accounts:transaction(Me#user.username, quota, kvs:get_config("accounts/default_quota", 300), #tx_default_assignment{}),
  114. kvs:put(Me#user{password = utils:sha(Me#user.password),
  115. starred = feed_create(),
  116. pinned = feed_create()})
  117. end || Me <- UserList],
  118. ?INFO("adding users to groups"),
  119. [ begin
  120. kvs_users:init_mq(Me#user.username, [GId1, GId2]),
  121. groups:add_to_group_directly_to_db(Me#user.username, GId1, member),
  122. groups:add_to_group_directly_to_db(Me#user.username, GId2, member)
  123. end || Me <- UserList ],
  124. acls:define_access({user, "maxim"}, {feature, admin}, allow),
  125. acls:define_access({user_type, admin}, {feature, admin}, allow),
  126. ?INFO("making all users each other friends"),
  127. [[case Me == Her of
  128. true -> ok;
  129. false -> kvs_users:subscr_user(Me#user.username, Her#user.username)
  130. end || Her <- UserList] || Me <- UserList].
  131. add_sample_packages() -> membership_packages:add_sample_data().
  132. version() -> ?INFO("version: ~p", [1]).
  133. % blocking
  134. block_user(Who, Whom) -> DBA=?DBA, DBA:block_user(Who, Whom).
  135. list_blocks(Who) -> DBA=?DBA, DBA:list_blocks(Who).
  136. unblock_user(Who, Whom) -> DBA=?DBA, DBA:unblock_user(Who, Whom).
  137. list_blocked_me(Me) -> DBA=?DBA, DBA:list_blocked_me(Me).
  138. is_user_blocked(Who, Whom) -> DBA=?DBA, DBA:is_user_blocked(Who, Whom).
  139. % configs
  140. add_configs() ->
  141. %% smtp
  142. kvs:put(#config{key="smtp/user", value="noreply@synrc.com"}),
  143. kvs:put(#config{key="smtp/password", value="maxim@synrc.com"}),
  144. kvs:put(#config{key="smtp/host", value="mail.synrc.com"}),
  145. kvs:put(#config{key="smtp/port", value=465}),
  146. kvs:put(#config{key="smtp/with_ssl", value=true}),
  147. kvs:put(#config{key="accounts/default_quota", value=2000}),
  148. kvs:put(#config{key="accounts/quota_limit/soft", value=-30}),
  149. kvs:put(#config{key="accounts/quota_limit/hard", value=-100}),
  150. kvs:put(#config{key="purchase/notifications/email", value=["maxim@synrc.com"]}),
  151. kvs:put(#config{key="delivery/notifications/email", value=["maxim@synrc.com"]}).
  152. put(Record) ->
  153. DBA=?DBA,
  154. DBA:put(Record).
  155. put_if_none_match(Record) ->
  156. DBA=?DBA,
  157. DBA:put_if_none_match(Record).
  158. update(Record, Meta) ->
  159. DBA=?DBA,
  160. DBA:update(Record, Meta).
  161. get(RecordName, Key) ->
  162. DBA=?DBA,
  163. case C = DBA:get(RecordName, Key) of
  164. {ok,_R} -> C;
  165. A -> A end.
  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. acls:define_access({user, U#user.username}, {feature, admin}, allow),
  238. acls:define_access({user_type, admin}, {feature, admin}, allow),
  239. ok.
  240. make_rich(User) ->
  241. Q = kvs:get_config("accounts/default_quota", 300),
  242. accounts:transaction(User, quota, Q * 100, #tx_default_assignment{}),
  243. accounts:transaction(User, internal, Q, #tx_default_assignment{}),
  244. accounts: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(["db", "group", GroupId, "update_group"] = Route,
  304. Message, #state{owner=ThisGroupOwner, type=Type} = State) ->
  305. ?INFO("queue_action(~p): update_group: Owner=~p, Route=~p, Message=~p", [self(), {Type, ThisGroupOwner}, Route, Message]),
  306. {_UId, _GroupUsername, Name, Description, Owner, Publicity} = Message,
  307. SanePublicity = case Publicity of
  308. "public" -> public;
  309. "moderated" -> moderated;
  310. "private" -> private;
  311. _ -> undefined
  312. end,
  313. SaneOwner = case kvs:get(user, Owner) of
  314. {ok, _} -> Owner;
  315. _ -> undefined
  316. end,
  317. {ok, #group{}=Group} = kvs:get(group, GroupId),
  318. NewGroup = Group#group{
  319. name = coalesce(Name,Group#group.name),
  320. description = coalesce(Description,Group#group.description),
  321. publicity = coalesce(SanePublicity,Group#group.publicity),
  322. owner = coalesce(SaneOwner,Group#group.owner)},
  323. kvs:put(NewGroup),
  324. {noreply, State};
  325. handle_notice(Route, Message, State) -> error_logger:info_msg("Unknown KVS notice").
  326. coalesce(undefined, B) -> B;
  327. coalesce(A, _) -> A.