Local changes from desktop
This commit is contained in:
parent
0d34959e08
commit
e28ab91935
|
@ -10,4 +10,5 @@ npm-debug.*
|
||||||
web-build/
|
web-build/
|
||||||
web-report/
|
web-report/
|
||||||
/dist
|
/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-keep-awake": "~9.1.2",
|
||||||
"expo-linear-gradient": "~9.1.0",
|
"expo-linear-gradient": "~9.1.0",
|
||||||
"expo-localization": "~10.1.0",
|
"expo-localization": "~10.1.0",
|
||||||
|
"i18n-js": "^3.8.0",
|
||||||
"immutable": "^4.0.0-rc.12",
|
"immutable": "^4.0.0-rc.12",
|
||||||
"moment": "^2.27.0",
|
"moment": "^2.27.0",
|
||||||
"msgpack-lite": "^0.1.26",
|
"msgpack-lite": "^0.1.26",
|
||||||
|
@ -23,7 +24,6 @@
|
||||||
"react": "16.13.1",
|
"react": "16.13.1",
|
||||||
"react-dom": "16.13.1",
|
"react-dom": "16.13.1",
|
||||||
"react-native": "0.63.4",
|
"react-native": "0.63.4",
|
||||||
"react-native-chart-kit": "^3.13.0",
|
|
||||||
"react-native-gesture-handler": "~1.10.2",
|
"react-native-gesture-handler": "~1.10.2",
|
||||||
"react-native-reanimated": "~2.1.0",
|
"react-native-reanimated": "~2.1.0",
|
||||||
"react-native-safe-area-context": "3.2.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",
|
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
"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": {
|
"node_modules/iconv-lite": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
"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": {
|
"node_modules/performance-now": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
|
@ -16379,11 +16376,6 @@
|
||||||
"node": ">=10.13.0"
|
"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": {
|
"node_modules/posix-character-classes": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
||||||
|
@ -16666,22 +16658,6 @@
|
||||||
"react": "16.13.1"
|
"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": {
|
"node_modules/react-native-gesture-handler": {
|
||||||
"version": "1.10.3",
|
"version": "1.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/hyphenate-style-name/-/hyphenate-style-name-1.0.4.tgz",
|
||||||
"integrity": "sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ=="
|
"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": {
|
"iconv-lite": {
|
||||||
"version": "0.6.2",
|
"version": "0.6.2",
|
||||||
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz",
|
||||||
"integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw=="
|
"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": {
|
"performance-now": {
|
||||||
"version": "2.1.0",
|
"version": "2.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
"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",
|
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-5.0.0.tgz",
|
||||||
"integrity": "sha512-40QW5YalBNfQo5yRYmiw7Yz6TKKVr3h6970B2YE+3fQpsWcrbj1PzJgxeJ19DRQjhMbKPIuMY8rFaXc8moolVw=="
|
"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": {
|
"posix-character-classes": {
|
||||||
"version": "0.1.1",
|
"version": "0.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
|
"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": {
|
"react-native-gesture-handler": {
|
||||||
"version": "1.10.3",
|
"version": "1.10.3",
|
||||||
"resolved": "https://registry.npmjs.org/react-native-gesture-handler/-/react-native-gesture-handler-1.10.3.tgz",
|
"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": "16.13.1",
|
||||||
"react-dom": "16.13.1",
|
"react-dom": "16.13.1",
|
||||||
"react-native": "0.63.4",
|
"react-native": "0.63.4",
|
||||||
"react-native-chart-kit": "^3.13.0",
|
|
||||||
"react-native-gesture-handler": "~1.10.2",
|
"react-native-gesture-handler": "~1.10.2",
|
||||||
"react-native-reanimated": "~2.1.0",
|
"react-native-reanimated": "~2.1.0",
|
||||||
"react-native-safe-area-context": "3.2.0",
|
"react-native-safe-area-context": "3.2.0",
|
||||||
|
|
|
@ -193,6 +193,7 @@ async function queryDeviceFirmwareVersion(swimTrackerHost) {
|
||||||
async function queryNewestFirmwareVersion() {
|
async function queryNewestFirmwareVersion() {
|
||||||
const QUERY_URL = "https://swimtracker-update.bauer.tech/VERSION";
|
const QUERY_URL = "https://swimtracker-update.bauer.tech/VERSION";
|
||||||
const result = await request({ url: QUERY_URL, responseType: "text" });
|
const result = await request({ url: QUERY_URL, responseType: "text" });
|
||||||
|
console.log("newest firmware version, got", result);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue