app.py 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257
  1. import contextlib
  2. import os
  3. import flask
  4. import sys
  5. import lc.config as c
  6. import lc.error as e
  7. import lc.model as m
  8. import lc.request as r
  9. import lc.view as v
  10. from lc.web import Endpoint, endpoint, render
  11. app = c.app.app
  12. @endpoint("/")
  13. class Index(Endpoint):
  14. def html(self):
  15. return render(
  16. "main",
  17. v.Page(
  18. title="main",
  19. content=render(
  20. "message",
  21. v.Message(
  22. title="Lament Configuration",
  23. message="Bookmark organizing for real pinheads.",
  24. ),
  25. ),
  26. user=self.user,
  27. ),
  28. )
  29. @endpoint("/auth")
  30. class Auth(Endpoint):
  31. def api_post(self):
  32. u, token = m.User.login(self.request_data(r.User))
  33. flask.session["auth"] = token
  34. return self.api_ok(u.base_url(), {"token": token})
  35. @endpoint("/login")
  36. class Login(Endpoint):
  37. def html(self):
  38. return render(
  39. "main", v.Page(title="login", content=render("login"), user=self.user,)
  40. )
  41. @endpoint("/logout")
  42. class Logout(Endpoint):
  43. def html(self):
  44. if "auth" in flask.session:
  45. del flask.session["auth"]
  46. raise e.LCRedirect("/")
  47. def api_post(self):
  48. if "auth" in flask.session:
  49. del flask.session["auth"]
  50. return self.api_ok("/")
  51. @endpoint("/u")
  52. class CreateUser(Endpoint):
  53. def html(self):
  54. if self.user:
  55. raise e.LCRedirect(f"/u/{self.user.name}")
  56. token = flask.request.args.get("token")
  57. if not token:
  58. raise e.LCRedirect("/")
  59. add_user = v.AddUser(token=token)
  60. return render(
  61. "main",
  62. v.Page(
  63. title="add user", user=self.user, content=render("add_user", add_user),
  64. ),
  65. )
  66. def api_post(self):
  67. token = flask.request.args["token"]
  68. req = self.request_data(r.NewUser).to_user_request()
  69. u = m.User.from_invite(req, token)
  70. flask.session["auth"] = req.to_token()
  71. return self.api_ok(u.base_url(), u.to_dict())
  72. @endpoint("/u/<string:slug>")
  73. class GetUser(Endpoint):
  74. def html(self, slug: str):
  75. u = m.User.by_slug(slug)
  76. pg = int(flask.request.args.get("page", 1))
  77. tags = u.get_tags()
  78. links, pages = u.get_links(as_user=self.user, page=pg)
  79. linklist = v.LinkList(links=links, pages=pages, tags=tags)
  80. return render(
  81. "main",
  82. v.Page(
  83. title=f"user {u.name}",
  84. content=render("linklist", linklist),
  85. user=self.user,
  86. ),
  87. )
  88. def api_get(self, slug: str):
  89. return m.User.by_slug(slug).to_dict()
  90. @endpoint("/u/<string:user>/config")
  91. class GetUserConfig(Endpoint):
  92. def html(self, user: str):
  93. u = self.require_authentication(user)
  94. status_msg = flask.request.args.get("m", None)
  95. if status_msg is not None:
  96. status_msg = int(status_msg)
  97. return render(
  98. "main",
  99. v.Page(
  100. title="configuration",
  101. content=render("config", u.get_config(status_msg)),
  102. user=self.user,
  103. ),
  104. )
  105. @endpoint("/u/<string:user>/invite")
  106. class CreateInvite(Endpoint):
  107. def api_post(self, user: str):
  108. u = self.require_authentication(user)
  109. invite = m.UserInvite.manufacture(u)
  110. return self.api_ok(f"/u/{user}/config", {"invite": invite.token})
  111. @endpoint("/u/<string:user>/password")
  112. class ChangePassword(Endpoint):
  113. def api_post(self, user: str):
  114. u = self.require_authentication(user)
  115. config_url = u.config_url()
  116. req = self.request_data(r.PasswordChange)
  117. try:
  118. req.require_match()
  119. except e.MismatchedPassword:
  120. raise e.LCRedirect(f"{config_url}?m=2")
  121. try:
  122. u.change_password(req)
  123. except e.BadPassword:
  124. raise e.LCRedirect(f"{config_url}?m=3")
  125. return self.api_ok(f"{config_url}?m=1")
  126. @endpoint("/u/<string:user>/l")
  127. class CreateLink(Endpoint):
  128. def html(self, user: str):
  129. url = flask.request.args.get("url", "")
  130. name = flask.request.args.get("name", "")
  131. defaults = v.AddLinkDefaults(name=name, url=url,)
  132. return render(
  133. "main",
  134. v.Page(
  135. title="login", content=render("add_link", defaults), user=self.user,
  136. ),
  137. )
  138. def api_post(self, user: str):
  139. u = self.require_authentication(user)
  140. req = self.request_data(r.Link)
  141. l = m.Link.from_request(u, req)
  142. return self.api_ok(l.link_url(), l.to_dict())
  143. @endpoint("/u/<string:user>/l/<string:link>")
  144. class GetLink(Endpoint):
  145. def api_get(self, user: str, link: str):
  146. u = self.require_authentication(user)
  147. l = u.get_link(int(link))
  148. return self.api_ok(l.link_url(), l.to_dict())
  149. def api_post(self, user: str, link: str):
  150. u = self.require_authentication(user)
  151. l = u.get_link(int(link))
  152. req = self.request_data(r.Link)
  153. l.update_from_request(u, req)
  154. raise e.LCRedirect(l.link_url())
  155. def api_delete(self, user: str, link: str):
  156. u = self.require_authentication(user)
  157. u.get_link(int(link)).delete_instance()
  158. return self.api_ok(u.base_url())
  159. def html(self, user: str, link: str):
  160. l = m.User.by_slug(user).get_link(int(link))
  161. return render(
  162. "main",
  163. v.Page(
  164. title=f"link {l.name}",
  165. content=render("linklist", v.LinkList([l.to_view(self.user)], [])),
  166. user=self.user,
  167. ),
  168. )
  169. @endpoint("/u/<string:slug>/l/<string:link>/edit")
  170. class EditLink(Endpoint):
  171. def html(self, slug: str, link: str):
  172. u = self.require_authentication(slug)
  173. l = u.get_link(int(link))
  174. return render(
  175. "main",
  176. v.Page(
  177. title="login",
  178. content=render("edit_link", v.SingleLink(l)),
  179. user=self.user,
  180. ),
  181. )
  182. @endpoint("/u/<string:user>/t/<path:tag>")
  183. class GetTaggedLinks(Endpoint):
  184. def html(self, user: str, tag: str):
  185. u = m.User.by_slug(user)
  186. pg = int(flask.request.args.get("page", 1))
  187. t = u.get_tag(tag)
  188. links, pages = t.get_links(as_user=self.user, page=pg)
  189. tags = u.get_related_tags(t)
  190. linklist = v.LinkList(links=links, pages=pages, tags=tags)
  191. return render(
  192. "main",
  193. v.Page(
  194. title=f"tag {tag}",
  195. content=render("linklist", linklist),
  196. user=self.user,
  197. ),
  198. )
  199. @endpoint("/u/<string:user>/import")
  200. class PinboardImport(Endpoint):
  201. def html(self, user: str):
  202. u = self.require_authentication(user)
  203. return render(
  204. "main",
  205. v.Page(
  206. title=f"import pinboard data", content=render("import"), user=self.user,
  207. ),
  208. )
  209. def api_post(self, user: str):
  210. u = self.require_authentication(user)
  211. if "file" not in flask.request.files:
  212. raise e.BadFileUpload("could not find attached file")
  213. file = flask.request.files["file"]
  214. if file.filename == "":
  215. raise e.BadFileUpload("no file selected")
  216. u.import_pinboard_data(file.stream)
  217. return self.api_ok(u.base_url())