app.py 8.4 KB

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