From 839607863b32d2bdebd2511e1fbbafb2b4c7b16a Mon Sep 17 00:00:00 2001 From: Andy Haden Date: Fri, 14 Jun 2019 14:39:49 -0700 Subject: [PATCH] use comma-api --- README.md | 4 +- package.json | 4 +- scripts/deploy.sh | 7 +- src/CanExplorer.js | 70 ++++---- src/api/comma-api.js | 57 ------- src/api/comma-auth.js | 52 ------ src/api/rlog.js | 18 +- src/api/routes.js | 45 ----- src/api/video.js | 26 --- src/components/Modals/OnboardingModal.js | 206 +++-------------------- src/components/RouteVideoSync.js | 7 +- src/config.js | 7 +- src/index.js | 20 ++- yarn.lock | 65 ++++++- 14 files changed, 171 insertions(+), 417 deletions(-) delete mode 100644 src/api/comma-api.js delete mode 100644 src/api/comma-auth.js delete mode 100644 src/api/routes.js delete mode 100644 src/api/video.js diff --git a/README.md b/README.md index a8974f6..07f80a9 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ * sudo apt-get install -y libusb-dev libudev-dev ruby-sass * yarn install -## Developemnt +## Development * yarn run sass * yarn start @@ -15,7 +15,7 @@ * npm version patch * git push # push version patch * yarn build -* scripts/deploy.sh +* CONTAINER=cabana scripts/deploy.sh # Create React App documentation diff --git a/package.json b/package.json index b515258..6a55278 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,10 @@ "private": true, "homepage": "https://community.comma.ai/cabana", "dependencies": { + "@commaai/comma-api": "^1.0.7", "@commaai/hls.js": "^0.12.2", "@commaai/log_reader": "^0.3.1", + "@commaai/my-comma-auth": "^1.0.3", "@commaai/pandajs": "^0.3.4", "ap": "^0.2.0", "aphrodite": "^1.2.1", @@ -71,7 +73,7 @@ "xtend": "^4.0.1" }, "scripts": { - "start": "react-app-rewired start", + "start": "PORT=3001 react-app-rewired start", "build": "react-app-rewired build", "build:staging": "env-cmd .env.staging react-app-rewired build", "test": "react-app-rewired test --env=jsdom", diff --git a/scripts/deploy.sh b/scripts/deploy.sh index cc1f7b7..be290fe 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,14 +1,17 @@ #!/bin/bash set -e +set -x + +CONTAINER=${CONTAINER:-cabana-staging} pushd build/ find . -not -name "*.map" -type f | while read f; do - az storage blob upload --account-name chffrdist --file "$f" --container-name cabana --name "$f" + az storage blob upload --account-name chffrdist --file "$f" --container-name $CONTAINER --name "$f" done popd pushd public find img -type f | while read f; do - az storage blob upload --account-name chffrdist --file "$f" --container-name cabana --name "$f" + az storage blob upload --account-name chffrdist --file "$f" --container-name $CONTAINER --name "$f" done popd diff --git a/src/CanExplorer.js b/src/CanExplorer.js index 415021b..4c52d7a 100644 --- a/src/CanExplorer.js +++ b/src/CanExplorer.js @@ -4,15 +4,14 @@ import PropTypes from "prop-types"; import cx from "classnames"; import { createWriteStream } from "streamsaver"; import Panda from "@commaai/pandajs"; - +import CommaAuth from "@commaai/my-comma-auth"; +import { raw as RawDataApi, drives as DrivesApi } from "@commaai/comma-api"; import { USE_UNLOGGER, PART_SEGMENT_LENGTH, STREAMING_WINDOW } from "./config"; import * as GithubAuth from "./api/github-auth"; -import * as auth from "./api/comma-auth"; import DBC from "./models/can/dbc"; import Meta from "./components/Meta"; import Explorer from "./components/Explorer"; -import * as Routes from "./api/routes"; import OnboardingModal from "./components/Modals/OnboardingModal"; import SaveDbcModal from "./components/SaveDbcModal"; import LoadDbcModal from "./components/LoadDbcModal"; @@ -53,7 +52,6 @@ export default class CanExplorer extends Component { messages: {}, selectedMessages: [], route: null, - routes: [], canFrameOffset: -1, firstCanTime: null, lastBusTime: null, @@ -123,10 +121,13 @@ export default class CanExplorer extends Component { componentWillMount() { const { dongleId, name } = this.props; - if (this.props.max && this.props.url) { + if (CommaAuth.isAuthenticated() && !name) { + // TODO link to explorer + this.showOnboarding(); + } else if (this.props.max && this.props.url) { // probably the demo! const { max, url } = this.props; - const { startTime } = Routes.parseRouteName(name); + const startTime = Moment(name, "YYYY-MM-DD--H-m-s"); const route = { fullname: dongleId + "|" + name, @@ -141,40 +142,35 @@ export default class CanExplorer extends Component { }, this.initCanData ); - } else if (auth.isAuthenticated() && !name) { - Routes.fetchRoutes() - .then(routes => { - const _routes = []; - Object.keys(routes).forEach(route => { - _routes.push(routes[route]); - }); - this.setState({ routes: _routes }); - if (!_routes[name]) { - this.showOnboarding(); - } - }) - .catch(err => { - this.showOnboarding(); - }); } else if (dongleId && name) { - Routes.fetchRoutes(dongleId) - .then(routes => { - if (routes && routes[name]) { - // this makes fullname = dongleId + '|' + name - const route = routes[name]; - const newState = { - route, - currentParts: [ - 0, - Math.min(route.proclog, PART_SEGMENT_LENGTH - 1) - ] - }; - this.setState(newState, this.initCanData); - } else { - this.showOnboarding(); - } + const routeName = dongleId + "|" + name; + let urlPromise; + if (this.props.url) { + urlPromise = Promise.resolve(this.props.url); + } else { + urlPromise = DrivesApi.getRouteInfo(routeName).then(function(route) { + return route.url; + }); + } + Promise.all([urlPromise, RawDataApi.getLogUrls(routeName)]) + .then(initData => { + let [url, logUrls] = initData; + const newState = { + route: { + fullname: routeName, + proclog: logUrls.length - 1, + start_time: Moment(name, "YYYY-MM-DD--H-m-s"), + url + }, + currentParts: [ + 0, + Math.min(logUrls.length - 1, PART_SEGMENT_LENGTH - 1) + ] + }; + this.setState(newState, this.initCanData); }) .catch(err => { + console.error(err); this.showOnboarding(); }); } else { diff --git a/src/api/comma-api.js b/src/api/comma-api.js deleted file mode 100644 index 0b8a49e..0000000 --- a/src/api/comma-api.js +++ /dev/null @@ -1,57 +0,0 @@ -// API gateway for `api.commadotai.com/v1` urls -import { getCommaAccessToken } from "./comma-auth"; - -const URL_ROOT = "https://api.commadotai.com/v1/"; -const ConfigRequest = require("config-request/instance"); - -const request = ConfigRequest(); - -var initPromise = init(); - -async function init() { - var token = await getCommaAccessToken(); - request.configure({ - baseUrl: URL_ROOT, - token: "JWT " + token, - jwt: false - }); -} - -export async function get(endpoint, data) { - await initPromise; - return new Promise((resolve, reject) => { - request.get( - endpoint, - { - query: data, - json: true - }, - errorHandler(resolve, reject) - ); - }); -} - -export async function post(endpoint, data) { - await initPromise; - return new Promise((resolve, reject) => { - request.post( - endpoint, - { - body: data, - json: true - }, - errorHandler(resolve, reject) - ); - }); -} - -function errorHandler(resolve, reject) { - return handle; - - function handle(err, data) { - if (err) { - return reject(err); - } - resolve(data); - } -} diff --git a/src/api/comma-auth.js b/src/api/comma-auth.js deleted file mode 100644 index ad1606e..0000000 --- a/src/api/comma-auth.js +++ /dev/null @@ -1,52 +0,0 @@ -import Cookies from "js-cookie"; -import storage from "localforage"; - -import { - COMMA_ACCESS_TOKEN_COOKIE, - COMMA_OAUTH_REDIRECT_COOKIE -} from "../config"; - -let isAuthed = false; -let useForage = true; - -export async function getCommaAccessToken() { - let token = getTokenInternal(); - if (!token) { - try { - token = await storage.getItem("authorization"); - } catch (e) { - useForage = false; - } - } - - if (token) { - isAuthed = true; - if (useForage) { - await storage.setItem("authorization", token); - } - } - - return token; -} - -// seed cache -getCommaAccessToken(); - -function getTokenInternal() { - if (typeof localStorage !== "undefined") { - if (localStorage.authorization) { - return localStorage.authorization; - } - } - return Cookies.get(COMMA_ACCESS_TOKEN_COOKIE); -} - -export function isAuthenticated() { - return isAuthed; -} - -export function authUrl() { - Cookies.set(COMMA_OAUTH_REDIRECT_COOKIE, window.location.href); - - return "https://community.comma.ai/forum/ucp.php?mode=login&login=external&oauth_service=google"; -} diff --git a/src/api/rlog.js b/src/api/rlog.js index c21f223..410acb8 100644 --- a/src/api/rlog.js +++ b/src/api/rlog.js @@ -1,13 +1,27 @@ -import * as CommaAPI from "./comma-api"; +import { raw as RawDataApi, request as Request } from "@commaai/comma-api"; +import CommaAuth from "@commaai/my-comma-auth"; import request from "simple-get"; const urlStore = {}; +var initPromise; +function ensureInit() { + if (!initPromise) { + initPromise = CommaAuth.init().then(function(token) { + Request.configure(token); + return Promise.resolve(); + }); + } + return initPromise; +} + export async function getLogURLList(routeName) { if (urlStore[routeName]) { return urlStore[routeName]; } - var data = await CommaAPI.get("route/" + routeName + "/log_urls"); + await ensureInit(); + + var data = await RawDataApi.getLogUrls(routeName); urlStore[routeName] = data; diff --git a/src/api/routes.js b/src/api/routes.js deleted file mode 100644 index 1631001..0000000 --- a/src/api/routes.js +++ /dev/null @@ -1,45 +0,0 @@ -import Moment from "moment"; -import * as CommaAuth from "./comma-auth"; - -const ROUTES_ENDPOINT = "https://api.commadotai.com/v1/{dongleId}/routes/"; - -function momentizeTimes(routes) { - for (let routeName in routes) { - routes[routeName].start_time = Moment(routes[routeName].start_time); - routes[routeName].end_time = Moment(routes[routeName].end_time); - } - return routes; -} - -export async function fetchRoutes(dongleId) { - // will throw errors from fetch() on HTTP failure - - if (dongleId === undefined) { - dongleId = "me"; - } - - const accessToken = await CommaAuth.getCommaAccessToken(); - if (accessToken) { - const endpoint = ROUTES_ENDPOINT.replace("{dongleId}", dongleId); - const headers = new Headers(); - headers.append("Authorization", `JWT ${accessToken}`); - - const request = new Request(endpoint, { headers }); - const resp = await fetch(request); - const routes = await resp.json(); - if ("routes" in routes) { - return momentizeTimes(routes.routes); - } - } - - return {}; -} - -export function cameraPath(routeUrl, frame) { - return `${routeUrl}/sec${frame}.jpg`; -} - -export function parseRouteName(name) { - const startTime = Moment(name, "YYYY-MM-DD--H-m-s"); - return { startTime }; -} diff --git a/src/api/video.js b/src/api/video.js deleted file mode 100644 index 4194830..0000000 --- a/src/api/video.js +++ /dev/null @@ -1,26 +0,0 @@ -const STREAM_VERSION = 2; - -function videoUrl(dongleId, hashedRouteName) { - return `${ - process.env.REACT_APP_VIDEO_CDN - }/hls/${dongleId}/${hashedRouteName}/index.m3u8?v=${STREAM_VERSION}`; -} - -function videoUrlForRouteUrl(routeUrlString) { - const url = new URL(routeUrlString); - - const pathParts = url.pathname.split("/"); - - const [dongleIdPrefixed, hashedRouteName] = pathParts.slice( - pathParts.length - 2 - ); - let dongleId = dongleIdPrefixed; - if (dongleIdPrefixed.indexOf("comma-") === 0) { - const [, dongleIdNoPrefix] = dongleIdPrefixed.split("comma-"); - dongleId = dongleIdNoPrefix; - } - - return videoUrl(dongleId, hashedRouteName); -} - -export default { videoUrl, videoUrlForRouteUrl }; diff --git a/src/components/Modals/OnboardingModal.js b/src/components/Modals/OnboardingModal.js index 73e7056..dbf86c9 100644 --- a/src/components/Modals/OnboardingModal.js +++ b/src/components/Modals/OnboardingModal.js @@ -3,9 +3,9 @@ import PropTypes from "prop-types"; import Moment from "moment"; import _ from "lodash"; import cx from "classnames"; +import CommaAuth from "@commaai/my-comma-auth"; -import * as auth from "../../api/comma-auth"; - +import { EXPLORER_URL } from "../../config"; import Modal from "../Modals/baseModal"; export default class OnboardingModal extends Component { @@ -25,18 +25,12 @@ export default class OnboardingModal extends Component { this.state = { webUsbEnabled: !!navigator.usb, viewingUsbInstructions: false, - pandaConnected: false, - chffrDrivesSearch: "", - chffrDrivesSortBy: "start_time", - chffrDrivesOrderDesc: true + pandaConnected: false }; this.attemptPandaConnection = this.attemptPandaConnection.bind(this); this.toggleUsbInstructions = this.toggleUsbInstructions.bind(this); - this.handleSortDrives = this.handleSortDrives.bind(this); - this.handleSearchDrives = this.handleSearchDrives.bind(this); - this.navigateToAuth = this.navigateToAuth.bind(this); - this.openChffrDrive = this.openChffrDrive.bind(this); + this.navigateToExplorer = this.navigateToExplorer.bind(this); } attemptPandaConnection() { @@ -52,53 +46,14 @@ export default class OnboardingModal extends Component { }); } - navigateToAuth() { - const authUrl = auth.authUrl(); - window.location.href = authUrl; + navigateToExplorer() { + window.location.href = EXPLORER_URL; } filterRoutesWithCan(drive) { return drive.can === true; } - handleSearchDrives(drive) { - const { chffrDrivesSearch } = this.state; - const searchKeywords = chffrDrivesSearch - .split(" ") - .filter(s => s.length > 0) - .map(s => s.toLowerCase()); - - return ( - searchKeywords.length === 0 || - searchKeywords.some( - kw => - drive.end_geocode.toLowerCase().indexOf(kw) !== -1 || - drive.start_geocode.toLowerCase().indexOf(kw) !== -1 || - Moment(drive.start_time) - .format("dddd MMMM Do YYYY") - .toLowerCase() - .indexOf(kw) !== -1 || - Moment(drive.end_time) - .format("dddd MMMM Do YYYY") - .toLowerCase() - .indexOf(kw) !== -1 - ) - ); - } - - handleSortDrives(key) { - if (this.state.chffrDrivesSortBy === key) { - this.setState({ chffrDrivesOrderDesc: !this.state.chffrDrivesOrderDesc }); - } else { - this.setState({ chffrDrivesOrderDesc: true }); - this.setState({ chffrDrivesSortBy: key }); - } - } - - openChffrDrive(route) { - window.location.search = `?route=${route.fullname}`; - } - renderPandaEligibility() { const { webUsbEnabled, pandaConnected } = this.state; const { attemptingPandaConnection } = this.props; @@ -123,140 +78,27 @@ export default class OnboardingModal extends Component { } } - renderChffrOption() { - const { routes } = this.props; - if (routes.length > 0) { - return ( -
-
-
- - this.setState({ chffrDrivesSearch: e.target.value }) - } - /> -
-

(Try: "Drives in San Francisco" or "Drives in June 2017")

-
-
-
-
-
this.handleSortDrives("start_time")} - > - Date -
-
this.handleSortDrives("end_geocode")} - > - Places -
-
- Time -
-
this.handleSortDrives("len")} - > - Distance -
-
-
-
    - {_.orderBy( - routes, - [this.state.chffrDrivesSortBy], - [this.state.chffrDrivesOrderDesc ? "desc" : "asc"] - ) - .filter(this.filterRoutesWithCan) - .filter(this.handleSearchDrives) - .map(route => { - const routeDuration = Moment.duration( - route.end_time.diff(route.start_time) - ); - const routeStartClock = Moment(route.start_time).format("LT"); - const routeEndClock = Moment(route.end_time).format("LT"); - return ( -
  • -
    - - {Moment(route.start_time._i).format("MMM Do")} - - {Moment(route.start_time._i).format("dddd")} -
    -
    - {route.end_geocode} - From {route.start_geocode} -
    -
    - - {routeDuration.hours > 0 - ? `${routeDuration._data.hours} hr ` - : null} - {`${routeDuration._data.minutes} min ${ - routeDuration._data.seconds - } sec`} - - {`${routeStartClock} - ${routeEndClock}`} -
    -
    - {route.len.toFixed(2)} mi - {(route.len * 1.6).toFixed(2)} km -
    -
    - -
    -
  • - ); - })} -
-
- ); - } else { - return ( - - ); - } + renderLogin() { + return ( + + ); } renderOnboardingOptions() { return (
-
{this.renderChffrOption()}
+
{this.renderLogin()}