Local changes from desktop
This commit is contained in:
parent
0d34959e08
commit
e28ab91935
|
@ -10,4 +10,5 @@ npm-debug.*
|
|||
web-build/
|
||||
web-report/
|
||||
/dist
|
||||
/venv
|
||||
venv
|
||||
__pycache__
|
|
@ -1,31 +0,0 @@
|
|||
aiosqlite==0.17.0
|
||||
alembic==1.6.5
|
||||
asgiref==3.4.1
|
||||
bcrypt==3.2.0
|
||||
cffi==1.14.6
|
||||
click==8.0.1
|
||||
databases==0.4.3
|
||||
Deprecated==1.2.12
|
||||
dnspython==2.1.0
|
||||
email-validator==1.1.3
|
||||
fastapi==0.68.0
|
||||
fastapi-users==6.1.2
|
||||
h11==0.12.0
|
||||
idna==3.2
|
||||
makefun==1.11.3
|
||||
Mako==1.1.4
|
||||
MarkupSafe==2.0.1
|
||||
ormar==0.10.16
|
||||
passlib==1.7.4
|
||||
pycparser==2.20
|
||||
pydantic==1.8.2
|
||||
PyJWT==2.1.0
|
||||
python-dateutil==2.8.2
|
||||
python-editor==1.0.4
|
||||
python-multipart==0.0.5
|
||||
six==1.16.0
|
||||
SQLAlchemy==1.3.23
|
||||
starlette==0.14.2
|
||||
typing-extensions==3.7.4.3
|
||||
uvicorn==0.15.0
|
||||
wrapt==1.12.1
|
|
@ -1,39 +0,0 @@
|
|||
from .db import database, engine, Base, DbSession
|
||||
from .users import add_user_routers, User
|
||||
from fastapi import FastAPI
|
||||
from .routes import create_api_router
|
||||
from starlette.requests import Request
|
||||
|
||||
|
||||
def get_app(database) -> FastAPI:
|
||||
application = FastAPI(title="swimtracker", debug=True, version="0.1")
|
||||
fastapi_users = add_user_routers(application, database)
|
||||
current_user = fastapi_users.current_user(active=True, verified=True)
|
||||
current_superuser = fastapi_users.current_user(active=True, superuser=True, verified=True)
|
||||
application.include_router(create_api_router(current_user))
|
||||
return application
|
||||
|
||||
|
||||
app = get_app(database)
|
||||
|
||||
|
||||
@app.middleware("http")
|
||||
async def db_session_middleware(request: Request, call_next):
|
||||
request.state.db = DbSession()
|
||||
response = await call_next(request)
|
||||
request.state.db.close()
|
||||
return response
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup() -> None:
|
||||
print("creating")
|
||||
Base.metadata.create_all(engine)
|
||||
if not database.is_connected:
|
||||
await database.connect()
|
||||
|
||||
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown() -> None:
|
||||
if database.is_connected:
|
||||
await database.disconnect()
|
|
@ -1,2 +0,0 @@
|
|||
DATABASE_URL = "sqlite:///db.sqlite"
|
||||
JWT_SECRET = "4SmRyfsvG86R9jZQfTshfoDlcxYlueHmkMXJbszp"
|
|
@ -1,20 +0,0 @@
|
|||
import databases
|
||||
import sqlalchemy
|
||||
from .config import DATABASE_URL
|
||||
from sqlalchemy.ext.declarative import DeclarativeMeta, declarative_base
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
from starlette.requests import Request
|
||||
|
||||
|
||||
database = databases.Database(DATABASE_URL)
|
||||
Base: DeclarativeMeta = declarative_base()
|
||||
|
||||
engine = sqlalchemy.create_engine(
|
||||
DATABASE_URL, connect_args={"check_same_thread": False}
|
||||
)
|
||||
|
||||
DbSession = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
def get_db(request: Request):
|
||||
return request.state.db
|
|
@ -1,41 +0,0 @@
|
|||
from .db import Base
|
||||
from sqlalchemy import Column, Integer, Index, LargeBinary, ForeignKey, and_, or_
|
||||
|
||||
|
||||
class Session(Base):
|
||||
__tablename__ = "session"
|
||||
|
||||
device_id = Column(Integer, primary_key=True)
|
||||
start_time = Column(Integer, primary_key=True)
|
||||
data = Column(LargeBinary(1024 * 1024 * 2), nullable=False)
|
||||
user = Column(ForeignKey("user.id"), nullable=False)
|
||||
|
||||
value_right_shift = Column(Integer)
|
||||
tare_value = Column(Integer)
|
||||
kg_factor = Column(Integer)
|
||||
|
||||
Index('device_id', 'start_time', unique=True)
|
||||
|
||||
|
||||
class FriendRequest(Base):
|
||||
__tablename__ = "friend_request"
|
||||
requesting_user = Column(ForeignKey("user.id"), primary_key=True)
|
||||
receiving_user = Column(ForeignKey("user.id"), primary_key=True)
|
||||
|
||||
|
||||
class Friendship(Base):
|
||||
__tablename__ = "friendship"
|
||||
user_id = Column(ForeignKey("user.id"), primary_key=True)
|
||||
friend_id = Column(ForeignKey("user.id"), primary_key=True)
|
||||
|
||||
@staticmethod
|
||||
def befriend(db, userid1, userid2):
|
||||
db.add(Friendship(user_id=userid1, friend_id=userid2))
|
||||
|
||||
@staticmethod
|
||||
def are_friends(db, userid1, userid2):
|
||||
query_filter = or_(
|
||||
and_(Friendship.user_id == userid1, Friendship.friend_id == userid2),
|
||||
and_(Friendship.user_id == userid2, Friendship.friend_id == userid1),
|
||||
)
|
||||
return db.query(Friendship).filter(query_filter).count() > 0
|
|
@ -1,95 +0,0 @@
|
|||
from . import models
|
||||
from . import schemas
|
||||
from .db import get_db
|
||||
from fastapi import APIRouter, Depends, HTTPException
|
||||
from fastapi import status
|
||||
from sqlalchemy.exc import IntegrityError
|
||||
from sqlalchemy.orm import Session as DbSession
|
||||
from sqlalchemy.orm.exc import NoResultFound
|
||||
from typing import List
|
||||
from .users import User, UserTable
|
||||
import base64
|
||||
|
||||
|
||||
def create_api_router(current_user):
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
@router.post("/sessions",
|
||||
response_model=schemas.Session,
|
||||
tags=["sessions"],
|
||||
status_code=status.HTTP_201_CREATED)
|
||||
def create_session(session: schemas.SessionBase,
|
||||
db: DbSession = Depends(get_db),
|
||||
user: User = Depends(current_user)):
|
||||
session_props = session.dict()
|
||||
session_props['user'] = user.id
|
||||
session_props['data'] = base64.b64decode(session_props['data'])
|
||||
db_obj = models.Session(**session_props)
|
||||
db.add(db_obj)
|
||||
db.commit()
|
||||
return db_obj
|
||||
|
||||
|
||||
@router.get("/sessions", response_model=List[schemas.Session], tags=["sessions"])
|
||||
def list_sessions(skip=0,
|
||||
limit=100,
|
||||
db: DbSession = Depends(get_db),
|
||||
user: User = Depends(current_user)):
|
||||
return db.query(models.Session).filter(models.Session.user == user.id).order_by(
|
||||
models.Session.start_time.desc()).offset(skip).limit(limit).all()
|
||||
|
||||
|
||||
@router.post("/friends/request_friendship/{user_id}", tags=["friends"])
|
||||
def create_friend_request(other_user_id: str,
|
||||
db: DbSession = Depends(get_db),
|
||||
user: User = Depends(current_user)):
|
||||
if models.Friendship.are_friends(db, other_user_id, user.id):
|
||||
raise HTTPException(status.HTTP_406_NOT_ACCEPTABLE, detail="already friends")
|
||||
|
||||
FR = models.FriendRequest
|
||||
friend_request_from_other_user = db.query(FR).filter(FR.requesting_user == other_user_id,
|
||||
FR.receiving_user == user.id).count()
|
||||
if friend_request_from_other_user > 0:
|
||||
raise HTTPException(status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail="Friend request exist from other user, accept it")
|
||||
else:
|
||||
try:
|
||||
new_friend_request = FR(requesting_user=user.id, receiving_user=other_user_id)
|
||||
db.add(new_friend_request)
|
||||
db.commit()
|
||||
return {"msg": "ok"}
|
||||
except IntegrityError:
|
||||
raise HTTPException(status.HTTP_406_NOT_ACCEPTABLE,
|
||||
detail="Friend request already exists")
|
||||
|
||||
|
||||
@router.post("/friends/accept_friendship/{user_id}", tags=["friends"])
|
||||
def accept_friend_request(other_user_id: str,
|
||||
db: DbSession = Depends(get_db),
|
||||
user: User = Depends(current_user)):
|
||||
FR = models.FriendRequest
|
||||
try:
|
||||
friend_request = db.query(FR).filter(FR.requesting_user == other_user_id,
|
||||
FR.receiving_user == user.id).one()
|
||||
except NoResultFound:
|
||||
raise HTTPException(status_code=404, detail="No matching friend request found")
|
||||
|
||||
models.Friendship.befriend(db, other_user_id, user.id)
|
||||
db.delete(friend_request)
|
||||
db.commit()
|
||||
return {"msg": "ok"}
|
||||
|
||||
|
||||
@router.get("/friends", tags=["friends"], response_model=schemas.FriendsInfo)
|
||||
def list_friends_info(db: DbSession = Depends(get_db), user: User = Depends(current_user)):
|
||||
user_obj = db.query(UserTable).filter(UserTable.id == user.id).one()
|
||||
return schemas.FriendsInfo(incoming_requests=user_obj.friend_requests_in,
|
||||
outgoing_requests=user_obj.friend_requests_out)
|
||||
|
||||
return router
|
||||
# todo: remove friend requests
|
||||
# todo: remove friendship
|
||||
# todo: search user by email
|
||||
# todo: add usernames to users
|
||||
# todo: search by username
|
|
@ -1,37 +0,0 @@
|
|||
from typing import Optional, List
|
||||
from pydantic import BaseModel, conint, UUID4
|
||||
from pydantic.networks import EmailStr
|
||||
|
||||
|
||||
class SessionBase(BaseModel):
|
||||
device_id: int
|
||||
start_time: conint(gt=1546297200)
|
||||
data: str
|
||||
|
||||
value_right_shift: Optional[conint(ge=0, le=32)]
|
||||
tare_value: Optional[conint(ge=0)]
|
||||
kg_factor: Optional[conint(ge=0)]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
||||
|
||||
|
||||
class UserInfo(BaseModel):
|
||||
id: UUID4
|
||||
email: EmailStr
|
||||
|
||||
|
||||
class Session(SessionBase):
|
||||
user: UUID4
|
||||
|
||||
|
||||
class FriendRequestCreate(BaseModel):
|
||||
other_user_id: int
|
||||
|
||||
|
||||
class FriendsInfo(BaseModel):
|
||||
incoming_requests: List[UserInfo]
|
||||
outgoing_requests: List[UserInfo]
|
||||
|
||||
class Config:
|
||||
orm_mode = True
|
|
@ -1,85 +0,0 @@
|
|||
from .config import JWT_SECRET
|
||||
from .db import Base
|
||||
from fastapi import Request
|
||||
from fastapi_users import FastAPIUsers, models
|
||||
from fastapi_users.authentication import JWTAuthentication
|
||||
from fastapi_users.db import SQLAlchemyBaseUserTable, SQLAlchemyUserDatabase
|
||||
from sqlalchemy.orm import relationship, backref
|
||||
|
||||
|
||||
class User(models.BaseUser):
|
||||
pass
|
||||
|
||||
|
||||
class UserCreate(models.BaseUserCreate):
|
||||
pass
|
||||
|
||||
|
||||
class UserUpdate(User, models.BaseUserUpdate):
|
||||
pass
|
||||
|
||||
|
||||
class UserDB(User, models.BaseUserDB):
|
||||
pass
|
||||
|
||||
|
||||
class UserTable(Base, SQLAlchemyBaseUserTable):
|
||||
#id = Column(Integer, primary_key=True)
|
||||
sessions = relationship("Session")
|
||||
friend_requests_in = relationship(
|
||||
"UserTable",
|
||||
secondary="friend_request",
|
||||
primaryjoin=("UserTable.id == FriendRequest.receiving_user"),
|
||||
secondaryjoin=("UserTable.id == FriendRequest.requesting_user"),
|
||||
backref=backref("friend_requests_out"))
|
||||
friends = relationship('UserTable',
|
||||
secondary="friendship",
|
||||
primaryjoin=("UserTable.id == Friendship.user_id"),
|
||||
secondaryjoin=("UserTable.id == Friendship.friend_id"))
|
||||
|
||||
|
||||
def add_user_routers(app, database):
|
||||
user_db = SQLAlchemyUserDatabase(UserDB, database, UserTable.__table__)
|
||||
jwt_authentication = JWTAuthentication(secret=JWT_SECRET,
|
||||
lifetime_seconds=60 * 60 * 8,
|
||||
tokenUrl="auth/jwt/login")
|
||||
|
||||
fastapi_users = FastAPIUsers(
|
||||
user_db,
|
||||
[jwt_authentication],
|
||||
User,
|
||||
UserCreate,
|
||||
UserUpdate,
|
||||
UserDB,
|
||||
)
|
||||
|
||||
def on_after_register(user: UserDB, request: Request):
|
||||
print(f"User {user.id} has registered.")
|
||||
|
||||
def on_after_forgot_password(user: UserDB, token: str, request: Request):
|
||||
print(f"User {user.id} has forgot their password. Reset token: {token}")
|
||||
|
||||
def after_verification_request(user: UserDB, token: str, request: Request):
|
||||
print(f"Verification requested for user {user.id}. Verification token: {token}")
|
||||
|
||||
app.include_router(fastapi_users.get_auth_router(jwt_authentication),
|
||||
prefix="/auth/jwt",
|
||||
tags=["auth"])
|
||||
|
||||
app.include_router(fastapi_users.get_register_router(on_after_register),
|
||||
prefix="/auth",
|
||||
tags=["auth"])
|
||||
app.include_router(
|
||||
fastapi_users.get_reset_password_router(JWT_SECRET,
|
||||
after_forgot_password=on_after_forgot_password),
|
||||
prefix="/auth",
|
||||
tags=["auth"],
|
||||
)
|
||||
app.include_router(
|
||||
fastapi_users.get_verify_router(JWT_SECRET,
|
||||
after_verification_request=after_verification_request),
|
||||
prefix="/auth",
|
||||
tags=["auth"],
|
||||
)
|
||||
app.include_router(fastapi_users.get_users_router(), prefix="/users", tags=["users"])
|
||||
return fastapi_users
|
|
@ -1,73 +0,0 @@
|
|||
import os
|
||||
from typing import Any, Generator
|
||||
|
||||
import pytest
|
||||
import databases
|
||||
from src.db import Base, get_db
|
||||
from src import get_app
|
||||
from fastapi import FastAPI
|
||||
from fastapi.testclient import TestClient
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.orm import sessionmaker
|
||||
|
||||
# Default to using sqlite in memory for fast tests.
|
||||
# Can be overridden by environment variable for testing in CI against other
|
||||
# database engines
|
||||
SQLALCHEMY_DATABASE_URL = os.getenv('TEST_DATABASE_URL', "sqlite://")
|
||||
|
||||
engine = create_engine(SQLALCHEMY_DATABASE_URL, connect_args={"check_same_thread": False})
|
||||
|
||||
Session = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def app() -> Generator[FastAPI, Any, None]:
|
||||
"""
|
||||
Create a fresh database on each test case.
|
||||
"""
|
||||
print(list(Base.metadata.tables.keys()))
|
||||
Base.metadata.create_all(engine) # Create the tables.
|
||||
_app = get_app(databases.Database(SQLALCHEMY_DATABASE_URL))
|
||||
yield _app
|
||||
Base.metadata.drop_all(engine)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def db_session(app: FastAPI) -> Generator[Session, Any, None]:
|
||||
"""
|
||||
Creates a fresh sqlalchemy session for each test that operates in a
|
||||
transaction. The transaction is rolled back at the end of each test ensuring
|
||||
a clean state.
|
||||
"""
|
||||
|
||||
# connect to the database
|
||||
connection = engine.connect()
|
||||
# begin a non-ORM transaction
|
||||
transaction = connection.begin()
|
||||
# bind an individual Session to the connection
|
||||
session = Session(bind=connection)
|
||||
yield session # use the session in tests.
|
||||
session.close()
|
||||
# rollback - everything that happened with the
|
||||
# Session above (including calls to commit())
|
||||
# is rolled back.
|
||||
transaction.rollback()
|
||||
# return connection to the Engine
|
||||
connection.close()
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def client(app: FastAPI, db_session: Session) -> Generator[TestClient, Any, None]:
|
||||
"""
|
||||
Create a new FastAPI TestClient that uses the `db_session` fixture to override
|
||||
the `get_db` dependency that is injected into routes.
|
||||
"""
|
||||
def _get_test_db():
|
||||
try:
|
||||
yield db_session
|
||||
finally:
|
||||
pass
|
||||
|
||||
app.dependency_overrides[get_db] = _get_test_db
|
||||
with TestClient(app) as client:
|
||||
yield client
|
|
@ -1,24 +0,0 @@
|
|||
from src.db import DbSession
|
||||
from src.schemas import Session
|
||||
from fastapi import FastAPI
|
||||
from src.db import DbSession
|
||||
from fastapi.testclient import TestClient
|
||||
from src.users import UserCreate, User
|
||||
from fastapi.encoders import jsonable_encoder
|
||||
|
||||
#----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# Tests to write
|
||||
# - User Flow: register, verify, login
|
||||
# - create, list, delete session
|
||||
# - friendship: query user by mail, create friend request, accept
|
||||
def test_register_user(app: FastAPI, db_session: DbSession, client: TestClient):
|
||||
req_data = jsonable_encoder(UserCreate(email="test@abc.com", password="password"))
|
||||
response = client.post("/auth/register", json=req_data)
|
||||
print(response.json())
|
||||
resp_user = User(**response.json())
|
||||
assert response.status_code == 201
|
||||
assert resp_user.is_active
|
||||
assert not resp_user.is_superuser
|
||||
assert not resp_user.is_verified
|
|
@ -15,6 +15,7 @@
|
|||
"expo-keep-awake": "~9.1.2",
|
||||
"expo-linear-gradient": "~9.1.0",
|
||||
"expo-localization": "~10.1.0",
|
||||
"i18n-js": "^3.8.0",
|
||||
"immutable": "^4.0.0-rc.12",
|
||||
"moment": "^2.27.0",
|
||||
"msgpack-lite": "^0.1.26",
|
||||
|
@ -23,7 +24,6 @@
|
|||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "0.63.4",
|
||||
"react-native-chart-kit": "^3.13.0",
|
||||
"react-native-gesture-handler": "~1.10.2",
|
||||
"react-native-reanimated": "~2.1.0",
|
||||
"react-native-safe-area-context": "3.2.0",
|
||||
|
@ -8573,6 +8573,11 @@
|
|||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||
},
|
||||
"node_modules/i18n-js": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.8.0.tgz",
|
||||
"integrity": "sha512-hDsGgPuvw/2P+lXSbOafAwspK8Ste8YrwuuUg17W3wEcO1JkQxBlPgsN1t2+852nTnz4YSYTjZc/1nAA2PC/nw=="
|
||||
},
|
||||
"node_modules/iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
|
@ -16090,14 +16095,6 @@
|
|||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
},
|
||||
"node_modules/paths-js": {
|
||||
"version": "0.4.11",
|
||||
"resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz",
|
||||
"integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA==",
|
||||
"engines": {
|
||||
"node": ">=0.11.0"
|
||||
}
|
||||
},
|
||||
"node_modules/performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
|
@ -16379,11 +16376,6 @@
|
|||
"node": ">=10.13.0"
|
||||
}
|
||||
},
|
||||
"node_modules/point-in-polygon": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
|
||||
"integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="
|
||||
},
|
||||
"node_modules/posix-character-classes": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||
|
@ -16666,22 +16658,6 @@
|
|||
"react": "16.13.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-chart-kit": {
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-3.13.0.tgz",
|
||||
"integrity": "sha512-XSWw+1I0A6N6zYy76M+tMswgd5sRjiBFyejGTJ07geKVMrSwXY7sb0WYu9Zjr0ee9u8cUun/PB4D9bGqT9f9Zg==",
|
||||
"dependencies": {
|
||||
"lodash": "^4.17.11",
|
||||
"paths-js": "^0.4.10",
|
||||
"point-in-polygon": "^1.0.1"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"prop-types": "> 15.6.0",
|
||||
"react": "> 16.7.0",
|
||||
"react-native": ">= 0.50.0",
|
||||
"react-native-svg": "> 6.4.1"
|
||||
}
|
||||
},
|
||||
"node_modules/react-native-gesture-handler": {
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
|
||||
|
@ -26655,6 +26631,11 @@
|
|||
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
||||
},
|
||||
"i18n-js": {
|
||||
"version": "3.8.0",
|
||||
"resolved": "https://registry.npmjs.org/i18n-js/-/i18n-js-3.8.0.tgz",
|
||||
"integrity": "sha512-hDsGgPuvw/2P+lXSbOafAwspK8Ste8YrwuuUg17W3wEcO1JkQxBlPgsN1t2+852nTnz4YSYTjZc/1nAA2PC/nw=="
|
||||
},
|
||||
"iconv-lite": {
|
||||
"version": "0.6.2",
|
||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
||||
|
@ -32495,11 +32476,6 @@
|
|||
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
||||
},
|
||||
"paths-js": {
|
||||
"version": "0.4.11",
|
||||
"resolved": "https://registry.npmjs.org/paths-js/-/paths-js-0.4.11.tgz",
|
||||
"integrity": "sha512-3mqcLomDBXOo7Fo+UlaenG6f71bk1ZezPQy2JCmYHy2W2k5VKpP+Jbin9H0bjXynelTbglCqdFhSEkeIkKTYUA=="
|
||||
},
|
||||
"performance-now": {
|
||||
"version": "2.1.0",
|
||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||
|
@ -32708,11 +32684,6 @@
|
|||
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
|
||||
},
|
||||
"point-in-polygon": {
|
||||
"version": "1.1.0",
|
||||
"resolved": "https://registry.npmjs.org/point-in-polygon/-/point-in-polygon-1.1.0.tgz",
|
||||
"integrity": "sha512-3ojrFwjnnw8Q9242TzgXuTD+eKiutbzyslcq1ydfu82Db2y+Ogbmyrkpv0Hgj31qwT3lbS9+QAAO/pIQM35XRw=="
|
||||
},
|
||||
"posix-character-classes": {
|
||||
"version": "0.1.1",
|
||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||
|
@ -33232,16 +33203,6 @@
|
|||
}
|
||||
}
|
||||
},
|
||||
"react-native-chart-kit": {
|
||||
"version": "3.13.0",
|
||||
"resolved": "https://registry.npmjs.org/react-native-chart-kit/-/react-native-chart-kit-3.13.0.tgz",
|
||||
"integrity": "sha512-XSWw+1I0A6N6zYy76M+tMswgd5sRjiBFyejGTJ07geKVMrSwXY7sb0WYu9Zjr0ee9u8cUun/PB4D9bGqT9f9Zg==",
|
||||
"requires": {
|
||||
"lodash": "^4.17.11",
|
||||
"paths-js": "^0.4.10",
|
||||
"point-in-polygon": "^1.0.1"
|
||||
}
|
||||
},
|
||||
"react-native-gesture-handler": {
|
||||
"version": "1.10.3",
|
||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
|
||||
|
|
|
@ -34,7 +34,6 @@
|
|||
"react": "16.13.1",
|
||||
"react-dom": "16.13.1",
|
||||
"react-native": "0.63.4",
|
||||
"react-native-chart-kit": "^3.13.0",
|
||||
"react-native-gesture-handler": "~1.10.2",
|
||||
"react-native-reanimated": "~2.1.0",
|
||||
"react-native-safe-area-context": "3.2.0",
|
||||
|
|
|
@ -193,6 +193,7 @@ async function queryDeviceFirmwareVersion(swimTrackerHost) {
|
|||
async function queryNewestFirmwareVersion() {
|
||||
const QUERY_URL = "https://swimtracker-update.bauer.tech/VERSION";
|
||||
const result = await request({ url: QUERY_URL, responseType: "text" });
|
||||
console.log("newest firmware version, got", result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue