import flask import lc.config as c import lc.error as e import lc.model as m import lc.request as r import lc.view as v from lc.web import Endpoint, endpoint, render app = c.app.app @endpoint("/") class Index(Endpoint): def html(self): pg = int(flask.request.args.get("page", 1)) links, pages = m.Link.get_all(as_user=self.user, page=pg) linklist = v.LinkList(links=links, pages=pages, user="", tags=[]) return render( "main", v.Page( title="main", content=render("linklist", linklist), user=self.user, ), ) @endpoint("/auth") class Auth(Endpoint): def api_post(self): u, token = m.User.login(self.request_data(r.User)) flask.session["auth"] = token return self.api_ok(u.base_url(), {"token": token}) @endpoint("/login") class Login(Endpoint): def html(self): return render( "main", v.Page( title="login", content=render("login"), user=self.user, ), ) @endpoint("/logout") class Logout(Endpoint): def html(self): if "auth" in flask.session: del flask.session["auth"] raise e.LCRedirect("/") def api_post(self): if "auth" in flask.session: del flask.session["auth"] return self.api_ok("/") @endpoint("/u") class CreateUser(Endpoint): def html(self): if self.user: raise e.LCRedirect(f"/u/{self.user.name}") token = flask.request.args.get("token") if not token: raise e.LCRedirect("/") add_user = v.AddUser(token=token) return render( "main", v.Page( title="add user", user=self.user, content=render("add_user", add_user), ), ) def api_post(self): token = flask.request.args["token"] req = self.request_data(r.NewUser).to_user_request() u = m.User.from_invite(req, token) flask.session["auth"] = req.to_token() return self.api_ok(u.base_url(), u.to_dict()) @endpoint("/u/") class GetUser(Endpoint): def html(self, slug: str): u = m.User.by_slug(slug) pg = int(flask.request.args.get("page", 1)) tags = u.get_tags() links, pages = u.get_links(as_user=self.user, page=pg) linklist = v.LinkList(links=links, user=slug, pages=pages, tags=tags) return render( "main", v.Page( title=f"user {u.name}", content=render("linklist", linklist), user=self.user, ), ) def api_get(self, slug: str): return m.User.by_slug(slug).to_dict() @endpoint("/u//config") class GetUserConfig(Endpoint): def html(self, user: str): u = self.require_authentication(user) status_msg = flask.request.args.get("m", None) if status_msg is not None: status_msg = int(status_msg) return render( "main", v.Page( title="configuration", content=render("config", u.get_config(status_msg)), user=self.user, ), ) @endpoint("/u//invite") class CreateInvite(Endpoint): def api_post(self, user: str): u = self.require_authentication(user) invite = m.UserInvite.manufacture(u) return self.api_ok(f"/u/{user}/config", {"invite": invite.token}) @endpoint("/u//password") class ChangePassword(Endpoint): def api_post(self, user: str): u = self.require_authentication(user) config_url = u.config_url() req = self.request_data(r.PasswordChange) try: req.require_match() except e.MismatchedPassword: raise e.LCRedirect(f"{config_url}?m=2") try: u.change_password(req) except e.BadPassword: raise e.LCRedirect(f"{config_url}?m=3") return self.api_ok(f"{config_url}?m=1") @endpoint("/u//l") class CreateLink(Endpoint): def html(self, user: str): u = self.require_authentication(user) url = flask.request.args.get("url", "") name = flask.request.args.get("name", "") tags = u.get_tags() defaults = v.AddLinkDefaults( user=user, name=name, url=url, all_tags=tags, ) return render( "main", v.Page( title="login", content=render("add_link", defaults), user=self.user, ), ) def api_post(self, user: str): u = self.require_authentication(user) req = self.request_data(r.Link) link = m.Link.from_request(u, req) return self.api_ok(link.link_url(), link.to_dict()) @endpoint("/u//l/") class GetLink(Endpoint): def api_get(self, user: str, link_id: str): u = self.require_authentication(user) link = u.get_link(int(link_id)) return self.api_ok(link.link_url(), link.to_dict()) def api_post(self, user: str, link_id: str): u = self.require_authentication(user) link = u.get_link(int(link_id)) req = self.request_data(r.Link) link.update_from_request(u, req) raise e.LCRedirect(link.link_url()) def api_delete(self, user: str, link_id: str): u = self.require_authentication(user) u.get_link(int(link_id)).full_delete() return self.api_ok(u.base_url()) def html(self, user: str, link_id: str): link = m.User.by_slug(user).get_link(int(link_id)) return render( "main", v.Page( title=f"link {link.name}", content=render( "linklist", v.LinkList([link.to_view(self.user)], [], user=user) ), user=self.user, ), ) @endpoint("/u//l//edit") class EditLink(Endpoint): def html(self, slug: str, link_id: str): u = self.require_authentication(slug) all_tags = u.get_tags() link = u.get_link(int(link_id)) return render( "main", v.Page( title="login", content=render("edit_link", v.SingleLink(link, all_tags)), user=self.user, ), ) @endpoint("/u//t/") class GetTaggedLinks(Endpoint): def html(self, user: str, tag: str): u = m.User.by_slug(user) pg = int(flask.request.args.get("page", 1)) t = u.get_tag(tag) links, pages = t.get_links(as_user=self.user, page=pg) tags = u.get_related_tags(t) linklist = v.LinkList(links=links, pages=pages, tags=tags, user=user) return render( "main", v.Page( title=f"tag {tag}", content=render("linklist", linklist), user=self.user, ), ) @endpoint("/u//search/") class GetStringSearch(Endpoint): def html(self, user: str, needle: str): u = m.User.by_slug(user) pg = int(flask.request.args.get("page", 1)) links, pages = u.get_string_search(needle=needle, as_user=self.user, page=pg) tags = u.get_tags() linklist = v.LinkList(links=links, pages=pages, tags=tags, user=user) return render( "main", v.Page( title=f"search for '{needle}'", content=render("linklist", linklist), user=self.user, ), ) @endpoint("/u//import") class PinboardImport(Endpoint): def html(self, user: str): _ = self.require_authentication(user) return render( "main", v.Page( title="import pinboard data", content=render("import"), user=self.user, ), ) def api_post(self, user: str): u = self.require_authentication(user) if "file" not in flask.request.files: raise e.BadFileUpload("could not find attached file") file = flask.request.files["file"] if file.filename == "": raise e.BadFileUpload("no file selected") u.import_pinboard_data(file.stream) return self.api_ok(u.base_url()) @endpoint("/service-worker.js") class ServiceWorker(Endpoint): def route(self, *args, **kwargs): return flask.send_file("../js/serviceWorker.js", mimetype="text/javascript") @endpoint("/add-link") class AddLink(Endpoint): def html(self): if not self.user: raise e.LCRedirect("/login") args = flask.request.args.copy() # Android sets the text field to the url only url = args.get("url", None) text = args.get("text", "") if url is None and text.startswith("http"): args["url"] = text del args["text"] args = "&".join(f"{key}={value}" for key, value in args.items()) raise e.LCRedirect(f"/u/{self.user.name}/l?{args}")