Flaskista Flask+Reactiin

  • Keskustelun aloittaja Keskustelun aloittaja Makis
  • Aloitettu Aloitettu
Tietokannan ja bäkkärin välisen yhteyden sain toimimaan database:3306 -osoitteella, mutta backend-osoite antaa selaimessa CORS-virheen. Terminaaliin ei kuitenkaan tule mitään, ei frontilta eikä bäkkäriltä.

Koodi:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://backend/api/login. (Reason: CORS request did not succeed). Status code: (null).

Myöskään muut sivut kuin etusivu ei toimi, tulee vaan not foundia.

Tietämättä yhtään, mistä noi serverit on tilattu ja mitä ne maksaa, mutta yks vaihtoehto on myös käyttää erillistä testipannua testaamiseen. Julkaisukonffit vois pitää identtisesti ja olla vaan avaamatta yhteyksiä ulkomaailmasta testipannulle pois lukien oma IP tai miten luvitus onkaan helpoin järjestää, että pääsee itse käsiksi. Resursseissa voi ja kannattaakin säästää, eli testille joku pieni kautta halpa kone, joka jaksaa pyöritellä sen verran kuormaa kuin testaamisessa tarvittee.

Muutenkin minusta parempaa käytäntöä, kun tuotantoserverillä ei testailla kautta häärätä mitään ylimääräisiä.
Tämä on harrastusprojekti, en viitsi maksaa toista serveriä omasta pussista. Ei tuo tuotantoserverikään kummoinen ole, 2xCPU, 2GB. Mutta käyttäjämäärätkin tulee olemaan tyyliin "muutama käyttäjä päivässä odotusarvoisesti".

Mutta vähän alkaa tuntua, että onko tässä mitään järkeä. Ehkä me vaan testataan tuotantokannassa suoraan.
 
Mjaahah, nyt en saa yhteyttä curlillakaan bäkkäriin. Ei kyllä pienen ihmisen järki riitä ymmärtämään tätä.

Jos minulla on oma networkki tuossa 172.20.0.0-avaruudessa, niin kuvittelisin, että jollain sellaisella saisi yhteyden? Jos mäppäys docker-composessa on 5005:5000, niin kuvittelisin, että bäkkäri löytyy 172.20.0.9:5005 -osoitteesta. Vaan ei löydy. Eikä löydy localhost:5005:stäkään. Frontti pukkaa siis CORS:ia tuolla backend-osoitteella (porttinumerolla tai ilman).

Tässä ei ole _mitään_ loogista. Vaikka kuinka yritän tavata noita yleensä paskoja ohjeita, niin mikään ei toimi kuten järki sanoisi. Nytkin tuolta awesome-composen reposta kopioin konffit, niin nyt tulos on se, että frontti ei ilmeisesti toimi, koska saan vain nginx:n etusivun auki, sovellus ei aukea millään osoitteella.


Kun minun pieni järkeni sanoo, että jos määrittelen docker-composessa oman ip-osoitteen, vaikkapa 172.20.0.8 ja porteiksi vaikka 3001:3000, niin jos otan selaimella yhteyttä osoitteeseen 172.20.0.8:3001, niin selaimen pyyntö menee tuon kontin sisään porttiin 3000.

Sitten jos minulla on toinen service, jonka nimi on sanotaan vaikka backend ja siellä on flask run ajossa, mikä käyttää porttia 5000, niin frontissa osoite backend:5000 ottaisi tuohon yhteyttä. Mutta ei onnistu, eikä onnistu ko backendiin yhteys muutenkaan.

Mutta nytkin minulla on porttimäärittelynä 3000 ja siitä tulee vain Unable to Connect. Ilman porttia tulee nginx:ää vaikka frontti kyllä lähtee käyntiin (laitoin ngixn:n conffiin myös portin 3000).

Lopputulos: ohjeet johtaa harhaan, esimerkikit ei toimi.
 
Lähdin vielä yksinkertaistamaan hommaa ja taas meni uudella tavalla rikki. Nyt ei lähde kuin tietokanta käyntiin ja vaikka sen status on healthy niin bäkkäri, jolla on healthcheck dependency, ei lähde käyntiin. Ja koska frontilla on dependency bäkkäriin, niin ei lähde sekään. Pois tuota bäkkärin dependecyä ei voi ottaa, koska tietokannan pitää olla ajossa ennen bäkkärin käynnistystä. Mitään virhettä missään ei tietenkään ole, koska sehän nyt helpottaisi aivan liikaa.
 
Tätä en hiffaa, mistä se mariadb sitten tulee? Sinällään siis itse kanta voi hävitä joka kerta kun kontti sammutetaan, koska kyse on testikannasta. Ja itse asiassa olisi helpompikin, niin päivitykset tapahtuu pelkästään kanta restarttaamalla (ok, luontitiedosto pitää kopioida paikalleen).

Se tulee sieltä julkisesta docker reposta josta tuo dockerfilen FROM sen myös hakee. Eipä noita itse tarvitse vääntää kasaan, jollei ole jotain suurempaa muuttamassa. Volume mountit ja ympäristömuuttujat docker compose tiedostosta riittää yleensä.

Jos minulla on oma networkki tuossa 172.20.0.0-avaruudessa, niin kuvittelisin, että jollain sellaisella saisi yhteyden? Jos mäppäys docker-composessa on 5005:5000, niin kuvittelisin, että bäkkäri löytyy 172.20.0.9:5005 -osoitteesta. Vaan ei löydy. Eikä löydy localhost:5005:stäkään. Frontti pukkaa siis CORS:ia tuolla backend-osoitteella (porttinumerolla tai ilman).

En ole itse docker composen kanssa määritellyt IP alueita vaan tyytynyt niihin mitä docker oletuksena tarjoaa. Mutta joka tapauksessa tuo ports-määritys lisää hostille palomuuriin nat säännön joka ohjaa siinä määritellyn portin liikenteen, joka tulee hostin IP osoitteeseen, kontin porttiin. Eli jos hostin IP on 192.168.1.1 niin curl http://192.168.1.1:5005/api/login/ pitäisi toimia. Localhostin käyttäminen on haastavaa koska se voi tarkoittaa kontainereiden kanssa tilanteesta riippuen vähän eri asioita.

Kun minun pieni järkeni sanoo, että jos määrittelen docker-composessa oman ip-osoitteen, vaikkapa 172.20.0.8 ja porteiksi vaikka 3001:3000, niin jos otan selaimella yhteyttä osoitteeseen 172.20.0.8:3001, niin selaimen pyyntö menee tuon kontin sisään porttiin 3000.

Määrittelet compose filessä niitä IP osoitteita mitä kontit saa sisäisesti, eli tuossa ylläkuvatussa tapauksessa hostin palomuurissa on nat sääntö joka ohjaa liikenteen 0.0.0.0:3001 -> 172.20.0.8:3000

Kannattaa myös huomata että nuo lyhyet nimet jotka syntyvät automaattisesti docker compose servicen nimestä eivät toimi kuin konttien väliseen liikennöintiin. Jos Reactiin on määritelty että backend löytyy osoitteesta https://backend:3000 niin selain johon frontti on avattu yrittää hakea IP-osoitetta DNS nimelle backend ja tuohan ei toimi jollet ole sitä jotenkin sille koneelle kertonut josta yhteyttä yritetään muodostaa. Kannattaa katsoa selaimen developer consolista network välilehdeltä mitä siellä taustalla tapahtuu.

Itse olen yleensä tehnyt nuo siten että edessä on kuormantasaaja joka ohjaa kyselyt oikeaan paikkaan, /api prefixillä backendille ja muut fronttiin. Mutta ei tämä ole välttämätöntä, saa sen toimimaan ilmankin.
 
Se tulee sieltä julkisesta docker reposta josta tuo dockerfilen FROM sen myös hakee. Eipä noita itse tarvitse vääntää kasaan, jollei ole jotain suurempaa muuttamassa. Volume mountit ja ympäristömuuttujat docker compose tiedostosta riittää yleensä.
Julkisessa versiossa tuntuu vain olevan sellainen ongelma, että se ei salli rootin käyttöä, mikä vähän hankaloittaa asioita.

Kannattaa katsoa selaimen developer consolista network välilehdeltä mitä siellä taustalla tapahtuu.
Tästähän minä sen CORSinkin huomasin.

Itse olen yleensä tehnyt nuo siten että edessä on kuormantasaaja joka ohjaa kyselyt oikeaan paikkaan, /api prefixillä backendille ja muut fronttiin. Mutta ei tämä ole välttämätöntä, saa sen toimimaan ilmankin.
Tässä tapauksessa tuskin mitään kuormantasaajaa tarvitaan, kun yhtäaikaisia käyttäjiä on yhden käden sormilla laskettava määrä, jos sitäkään.

Jaahas, mariadb:latest-paketissa ei sitten ilmeisesti ole mitään työkaluja ja siksi healthcheck feilaa (ei löydy mysql:ää saati mysqladminia). Mikähän minulla oli tuossa aiemmin repona.
 
Ei ymmärrä ei. Otin mallia internetsistä ja tein tuon db-kohdan uusiksi niin, että siinä olisi volumet ym eikä Dockerfileä käytetä ollenkaan. Lähtee muka käyntiin, mutta mistä ip-osoiteesta tuon nyt sitten pitäisi löytyä? ip addr:n antama IP ei kelpaa. localhost ei kelpaa. Mikäs tässäkin on vikana?
Koodi:
  database:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: suomisf
      MYSQL_USER: test
      MYSQL_PASSWORD: password
    volumes:
      - data:/var/lib/mysql
    ports:
      - "3306:3306"
    healthcheck:
      test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "root", "-ppassword"]
      timeout: 3s
      retries: 20

volumes:
  data:
Nyt nimittäin docker system prune -a + docker compose up jää jumiin tällaiseen:

✔ Network sfbib_default Created 0.1s
✔ Container sfbib-database-1 Created 0.1s
✔ Container sfbib-backend-1 Created 0.0s
✔ Container sfbib-frontend-1 Created 0.0s
Attaching to sfbib-backend-1, sfbib-database-1, sfbib-frontend-1

En edes tiedä, mistä tuo sfbib_default oikein tulee.

Ei kun nyt vasta tajusin, että mistä hitosta tuo nuo nimet kaivaa? Olen ajanut docker system prune -a:ta monet kerrat ja tuollaisia nimiä ei löydy enää mistään. Onko jossain edelleen joku jemma, josta se kaivaa jotain vanhoja konffeja?
 
Viimeksi muokattu:
Ei jumalauta. Tyhjensin kaikki mahdolliset cachet sun muut, stoppasin kaikki dockeriin viittavat, restarttasin servicen ja sama tilanne. Onko tämä jotain vittuilua vai mikä homma tämä on? Mistä perseestä tuo "sfbib-backend-1" on kaivettu? Imagen nimi docker-composessa on sfbib-be. Tuota "sfbib-backend":iä ei todellakaan ole missään.

Hupaisaa on tässä sekin, että nyt ei docker compose down tee mitään, vaan pitää ctrl-c:llä tappaa tuo compose ja sen jälkeen kill -9:llä vielä prosessi.
 
Taitaa tulla nimen alkuosa kansion nimestä missä sinun docker-compose.yml on, ja loppu on servicen nimi, eli ylimmän tason elementti. Ja ykkönen on koska se on ensimmäinen instanssi, kun ainakin docker swarmissa pystyi laittamaan monta kopiota käyntiin.

container_name: jos haluat sen itse asettaa joksikin muuksi

Helpompaa on jos luovut konttien välisissä konffeissa ip osoitteista, ja käytät servicejen nimiä, sen pitäisi siellä konttiverkon sisällä resolvata. Ja käyttä konttien välillä sisäisiä portteja, ei ulkoisia uudelleenohjattuja.

Tuolla composella backend näkee kannan osoitteella database portissa 3306.
 
Otin kaikki nuo pois mutta jumii tuossa samassa kohdassa. Ilmeisesti tuo healthcheck ei mene vaan läpi, jos ajan pelkästään tuon tietokannan ylös, niin tila on starting. Mistä osoittteesta tuon pitäisi löytyä hostista? Kokeilin hostin ip:tä + tuota porttia ym. mutta en saa yhteyttä. Jos avaan shellin, niin sieltä ei tosiaan löydy mitään työkaluja.
 
On kyllä jännä, miten maailman simppeleimmätkin asiat on tehty näin vaikeaksi. Tuossa mariadb:latest -paketissa ei ilmeisesti ole mysqladminia mukana. No, mitenpä tuohon asennat paketteja? En tiedä, google ei tunnu tietävän. Joten mitenkä teet healthcheckin? Ei tiedä googlekaan, kaikki ehdotukset olettaa että paketissa on mysqladmin. Minulla:
Koodi:
$ docker exec -it 272a5e8f47ab sh
# mysqladmin
sh: 1: mysqladmin: not found
Ja näköjään se, että jotain komentoa ei saada ajettua ei aiheuta tarvetta logittamiseen. Eli systeemi jää jumiin koska checkin vaatimaa softaa ei löydy, mutta asiaa ei vaivauduta mitenkään kertomaan minulle.
 
Jaahas, nyt ollaan takaisin siinä CORSissa. Saan tietokannan auki komennolla
docker exec -it 318a7b02cc94 mariadb --user root -ppassword
ja bäkkärikin vastaa curliin, frontti aukeaa selaimeen (localhost:3001) ja alasivut toimivat.

Mutta kun frontti yrittää ottaa yhteyttä bäkkäriin, niin selaimeen pamahtaa vain CORS.
 
Äh. Pitääkö se nginx sitten virittää kuitenkin tuolle bäkkärille? Flaskissa on CORS-moduli ollut käytössä ja laitoin vielä äsken maksimit luvat:
Koodi:
CORS(app, resources={r"/api/*": {"origins": "*", "allow_headers": "*", "expose_headers": "*"}})
mutta ei vaikutusta.
 
Hm, vika muuten on varmaan siinä, että eihän se selain ymmärrä tuon backendin päälle. Mutta jos ei ole omaa verkkoa, niin mikä osoite tuolle Reactille pitäisi backendiä varten antaa? Kokeilin jo komentoriviltä 127.0.0.1:5005, mutta tuo ei vastaa. Varmaan se IP, jonka kontti on saanut, toimii, mutta sehän vaihtuu joka kerta kun kontin käynnistää uudestaan.
 
Eli siis oikeastaan monen päivän ähellyksen jälkeen palasin takaisin siihen mistä lähdin. Kontit toimii, mutta front->back tuottaa CORSia. Ainoa parannus, minkä sain aikaan on se, että tietokanta ei nyt tarvitse omaa Dockerfileä.
 
Tässä tapauksessa tuskin mitään kuormantasaajaa tarvitaan, kun yhtäaikaisia käyttäjiä on yhden käden sormilla laskettava määrä, jos sitäkään.

Ei tarvitakaan, olen vaan tykännyt siinä asettaa erilaisia headereita kuten Content Policyn ja HSTS:n. Plus voi näppärästi rajoittaa liikennettä.

Hm, vika muuten on varmaan siinä, että eihän se selain ymmärrä tuon backendin päälle. Mutta jos ei ole omaa verkkoa, niin mikä osoite tuolle Reactille pitäisi backendiä varten antaa? Kokeilin jo komentoriviltä 127.0.0.1:5005, mutta tuo ei vastaa. Varmaan se IP, jonka kontti on saanut, toimii, mutta sehän vaihtuu joka kerta kun kontin käynnistää uudestaan.

Reactille pitää antaa se osoite josta selain pääsee kiinni backendiin, eli palvelimen julkinen IP ja compose filessä määritelty ulkoinen portti.
 
Mutta mikä se palvelimen julkinen IP on kun se pyörii kontissa samassa koneessa? Ei ole tarvetta tarjota tätä konttia ulkopuolelle.
 
Mutta mikä se palvelimen julkinen IP on kun se pyörii kontissa samassa koneessa? Ei ole tarvetta tarjota tätä konttia ulkopuolelle.

No mistäs sitten otat yhteyttä, pyöriikö selainkin palvelimella? Frontin koodiahan ajetaan selaimessa, ei se palvelimen päässä ota yhteyttä backendiin.
 
No joo, tässä tapauksessa ajoin lokaalisti mutta tarvitsenhan minä tosiaan palvelimellekin tämän. Siellä on nginx jo pyörimässä.
 
Hä hää. Sainpa toimimaan lokaalistikin. Bäkkärin Dockerfileessä oli vielä bugi, asetin FLASK_RUN_PORT:n siellä 5005:een. Poistin tuon rivin niin localhost:5005 alkoi toimimaan ja samalla frontti kykenee keskustelemaan bäkkärin kanssa. Tällaisilla fileillä homma siis onnistui.

docker-compose.yml:
Koodi:
services:
  frontend:
    image: 'sfbib-fe'
    build:
      context: ./suomisf-ui
      dockerfile: Dockerfile
    ports:
      - "3001:3000"
    depends_on: 
      - backend
    entrypoint: ["npm", "start"]

  backend:
    image: 'sfbib-be'
    build:
      context: ./suomisf
      dockerfile: Dockerfile
    ports:
      - "5005:5000"
    depends_on:
      database:
        condition: service_healthy
    stop_signal: SIGINT

  database:
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: password
      MYSQL_DATABASE: suomisf
      MYSQL_USER: user
      MYSQL_PASSWORD: password
      MARIADB_MYSQL_LOCALHOST_USER: 1
    volumes:
      - data:/var/lib/mysql
      - ./suomisf/backup/backup.sql:/docker-entrypoint-initdb.d/init-script.sql
    ports:
      - "3307:3306"
    healthcheck:
      test: ["CMD", "/usr/local/bin/healthcheck.sh", "--connect"]
      timeout: 3s
      retries: 20

volumes:
  data:
frontend/Dockerfile
Koodi:
# Use the official Node.js base image
FROM node:latest AS build

# Set the working directory in the container
WORKDIR /build

# Copy package.json and package-lock.json to the working directory
COPY package.json package.json
COPY yarn.lock yarn.lock

# Install the dependencies
RUN yarn install --production=false

COPY . .
frontend/.env
Koodi:
REACT_APP_SITE_URL=http://frontend/
REACT_APP_API_URL=http://localhost:5005/api/
REACT_APP_IMAGE_URL=https://www.sf-bibliografia.fi/
backend/Dockerfile
Koodi:
FROM --platform=$BUILDPLATFORM python:3.8.0 AS builder

WORKDIR /app
COPY ./requirements.txt requirements.txt

RUN pip3 install -r requirements.txt

COPY . .

ENV FLASK_RUN_HOST 0.0.0.0

EXPOSE 5005

CMD ["flask", "run"]
backend/.env
Koodi:
USER='root'
PASSWORD='password'
DATABASE_URL='mysql+pymysql://user:password@database:3306/suomisf'
JWT_SECRET_TOKEN="secret"
 
Hot reloadia en saanut toimimaan, CHOKIDAR_USEPOLLING=true environmentissa ei tuntunut tekevän mitään. Ilmeisesti tuo moduli pitäisi asentaa, mutta dokumentaation perusteella vie resursseja, joten en taida edes haluta serverille.

Vaihtoehto olisi konffata VS Code käyttämään konttia, mutta saa nähdä viitsinkö enää sitä.
 
Joo, joku tuossa VS Code konttihommassa minulla mättää. Vaikka avaan sen containerina (dev containers: open folder tjsp), mikä kyllä näyttäisi luovan uuden kontin, niin VS Code mm. valittaa joka ikisestä komponentista, että niitä ei löydy. Voin kyllä myös startata tuon frontin, mutta en keksi mikä se osoite mahtaa olla. localhost:3001 avaa selvästi tuohon olemassaolevaan.


Jos yritän attachoitua olemassaolevaan konttiin, niin kansion valinnan jälkeen VS Code jämähtää.
 
Sain riippuvuudetkin kuntoon suurin piirtein näin (eli ulkomuistista), kaikki ajettuna VS Coden terminaalissa, kun projekti avattu containerissa:
Koodi:
apt update
apt install npm # Oli jostain syystä niin vanha npm, että ei suostunut asentamaan enää mitään
npm ci
Tämän jälkeen VSC:n uudelleenkäynnistys.

Oikeasti piti tehdä vähän enemmän. Minulla ollut yarn käytössä, mutta tuossa se ei toiminut, joten yarn installin jälkeen piti vielä deletoida node_modules kertaalleen ja ajaa tuo npm ci.
 
On tämä kyllä ihan käsittämättömän monimutkaiseksi osattu tehdä. Ja rajapinnat rikotaan varmuuden vuoksi ilmeisesti puolen vuoden välein.

Olen tässä illan yrittänyt saada refresh tokeneita toimimaan. Ehkä sain homman Flaskin puolella onnistumaan (varmahan en voi olla, mutta homma vaikuttaa suhteellisen simppeliltä), mutta Reactiin löytyy n. miljoona erilaista konstia tehdä homma, yleensä Reactin interceptoreilla. Yksikään näistä vaan ei toimi, siis ei kelpaa edes TypeScriptille. Edes TypeScriptillä kirjoitetut.

Kyse noinkin periaatteellisesta hommasta, niin tyhmempi kuvittelisi, että tämä olisi tehty tavalla, joka on simppeli ja joka ei muutu heti kun yksikin komponentti jossain joskus päivittyy.

Nämä kaksi jo kävin läpi:
How to Refresh Json Web Tokens (JWT) using Axios Interceptors (ei toimi, koska on JS:ää, kaivele siinä TS-määrittelyt jostain)
Implement refresh token with JWT in React App using Axios (ei kelpaa TS:lle, joku kommenteissa oli huomannut saman mutta ilmeisesti kirjoittajaa ei enää kiinnosta)

Pitänee kokeilla sitten tätä seuraavaksi: axios-auth-refresh.

Taas saanut muutaman tunnin hukattua elämästä aivan typerään hommaan.
 
En tiedä, pää tässä taitaa hajota. Sellaista vinkkiä ei löydy, joka toimisi, mutta eipä nuo interceptorit tunnu toimivan muutenkaan. Minulla on testin vuoksi näinkin simppeli:
Koodi:
authApi.interceptors.response.use(
    (response: AxiosResponse<any>) => {
        return response;
    },
    async (error: AxiosError<any>) => {
        return Promise.reject(error);
}
Ja lopputuloksena on "Uncaught Error Error: Attempting to use a disconnected port object". Aha. "Error Error". Molopäät ei osaa edes virheilmoituksia kirjoittaa. Ja joku netissä valittikin, että Axios tuntuu vaihtuvan viikottain eikä mikään sitten enää toimi. Yritä siinä sitten löytää se toimiva tapa just siihen versioon joka itsellä on. Versiohistorian mukaan tuolla ei kyllä pitäisi olla mitään tämän käyttämäni 1.3.3:n ja 1.4.0:n välillä, joka liittyisi mitenkään tähän.

Sekin on totaalisen perseestä, että löytyy useampia blogikirjoituksia, jossa tämä homma muka tehdään TypeSscriptillä ja paskat tehdään. Sitä koodia kun tarjoilee kääntäjälle, niin pelkkää virhettä pukkaa. Esimerkiksi
Koodi:
const errMessage = error.response.data.message as string;
Ei todellakaan kelpaa (React Query, and Axios Interceptors JWT Authentication - CodevoWeb).
Samaisessa kohdassa myös viitataan _retry -kenttään, jota ei error.config:lla ole (tsekkasin koodista). Ja tämä sivu on toukokuulta.

Ymmärtäisin, jos minulla on joku määrittely väärin, Error ei olekaan AxiosError ja response AxiosResponse, mutta edes debuggeri ei näytä noita puuttuvia kenttiä joten olen taipuvainen epäilemään että niitä ei ole.

Olennaistahan tuossa olisi löytää se virheteksti, että voi päätellä, että kyse on vanhentuneesta tokenista, mutta en kyllä sitäkään löydä tuosta tietorakenteesta mistään. Löytyy vain "Network Error".
 
En tiedä, pää tässä taitaa hajota. Sellaista vinkkiä ei löydy, joka toimisi, mutta eipä nuo interceptorit tunnu toimivan muutenkaan. Minulla on testin vuoksi näinkin simppeli:
Koodi:
authApi.interceptors.response.use(
    (response: AxiosResponse<any>) => {
        return response;
    },
    async (error: AxiosError<any>) => {
        return Promise.reject(error);
}
Ja lopputuloksena on "Uncaught Error Error: Attempting to use a disconnected port object". Aha. "Error Error". Molopäät ei osaa edes virheilmoituksia kirjoittaa. Ja joku netissä valittikin, että Axios tuntuu vaihtuvan viikottain eikä mikään sitten enää toimi. Yritä siinä sitten löytää se toimiva tapa just siihen versioon joka itsellä on. Versiohistorian mukaan tuolla ei kyllä pitäisi olla mitään tämän käyttämäni 1.3.3:n ja 1.4.0:n välillä, joka liittyisi mitenkään tähän.

Taitaapi olla vika jossain ihan muualla kun Axioksessa. Esimerkkikoodi heidän dokumentaatiostaan toimii itsellä vallan mainiosti.

Sekin on totaalisen perseestä, että löytyy useampia blogikirjoituksia, jossa tämä homma muka tehdään TypeSscriptillä ja paskat tehdään. Sitä koodia kun tarjoilee kääntäjälle, niin pelkkää virhettä pukkaa. Esimerkiksi
Koodi:
const errMessage = error.response.data.message as string;
Ei todellakaan kelpaa (React Query, and Axios Interceptors JWT Authentication - CodevoWeb).
Samaisessa kohdassa myös viitataan _retry -kenttään, jota ei error.config:lla ole (tsekkasin koodista). Ja tämä sivu on toukokuulta.

Ymmärtäisin, jos minulla on joku määrittely väärin, Error ei olekaan AxiosError ja response AxiosResponse, mutta edes debuggeri ei näytä noita puuttuvia kenttiä joten olen taipuvainen epäilemään että niitä ei ole.

Olennaistahan tuossa olisi löytää se virheteksti, että voi päätellä, että kyse on vanhentuneesta tokenista, mutta en kyllä sitäkään löydä tuosta tietorakenteesta mistään. Löytyy vain "Network Error".
TypeScriptiä suosittelen käyttämään devatessa lähinnä lintterinä. En koe ainakaan itse lainkaan hyödylliseksi, että tyyppivirheet estävät applikaation kääntymisen kokonaan.
 
Tuossa toisessa tapauksessa sovellus oli mennyt johonkin ihmeellisen perätilaan, piti restartata. Mutta en kyllä näytä saavan tätä toimimaan jos tokeni on headereissa. Kun alkoi näyttää, että homma voisi toimia Reactin puolella, niin se ei toimi enää ollenkaan Flaskissa. Vaikka Authorization on taatusti viestissä mukana refresh tokeneineen, niin Flask palauttaa (jo ennen kuin oma koodini ajetaan) "Authorization Header Missing".

Joten ajattelin kokeilla cookieita. Ja voi jeesus. _Taas_ uusi komponentti pitäisi ilmeisesti opetella. Nyt kun nimittäin yritän tuota, niin "Missing cookie "access_token_cookie". Ja ei, JWT_COOKIE_SECURE:n asettaminen Falseksi ei auta.
 
Näyttäisi lopulta useamman päivän säätämisen jälkeen toimivan. Toki en tätä kokopäiväisesti tehnyt, lomalla muutakin tekemistä, mutta olihan tuo savotta. Ja ilman cookieita mennään, eli headerissa on tokenit edelleen.

Seuraavaksi varmaan pitää opiskella joku Flaskiin menevä datan tarkistin. Kirjoitin tuon yhden tallennusmetodin ja lopputulos oli niin monimutkainen että ei ole kyllä hirveästi luottoa... toki voisi kirjoitella yksikkötestejä, mutta kun tuo Copilot ei oikein osannut SQLAlchemyyn osannut tarjota toimivaa ratkaisua, eli pitäisi vissiin käsin kirjoittaa kaikki niin tuossa menisi melkoinen tovi. Keissejä tulisi varmaan joku 50. Ja eipä nämä toisiaan poissulkevia ole. Mutta ensin pitäisi selvittää, että mikä eri vaihtoehdoista on edelleen ylläpidetty ja oikeasti toimii.
 
Eikös ton axioksen voi heittää mäkeen ja käyttää ihan fetchiä? Teet oman pienen http-wrapperin sen ympärille. Tuntuu vaan helpottavan elämää mitä enemmän kirjastoja poistaa käytöstä nykyään.
 
Axiosta kyllä kehotetaan joka paikassa käyttämään. Eipä tuo itsessään ole edes kovin kummoinen käyttää. Interceptorit oli uusi juttu, mutta peruskäyttö oikeastaan hyvin simppeliä.
 
Koitin saada etusivua korjattua, mutta en kyllä käsitä, miksi tuo edelleen hajoaa kun sivusta tekee tarpeeksi kapean. Jostain syystä tekstit ei enää mahdu sivulle. Omasta mielestäni olen määritellyt, että leveässä ikkunassa on kaksi saraketta, tekstit ym. vasemmalla ja kuva oikealla. Kapeammassa nuo on alekkain ja kyllähän tuo kuva siirtyy alle, mutta yläosa (eli vasen sarake) on ihan väärin.

 
Veikkaan että grid- ja col-classit eivät ole sallittuja samassa elementissä, ainakin tuon PrimeFlexin dokumentaation esimerkkien mukaan sisäkkäinen grid on oma elementtinsä parent-gridin col-elementin sisällä.
 
Hm, olen kyllä noita käyttänyt monessakin paikassa, mutta muualla en ole huomannut ongelmia. Saatat silti olla oikeassa.

Mutta ainakin flexillä tuo on mahdollista, esim PrimeFlex - Utility-First CSS Library. Ja sivusto sanoo, että gridi on flex based ja määritellään näin:
Koodi:
display: flex;
flex-wrap: wrap;
margin-right: -0.5rem;
margin-left: -0.5rem;
margin-top: -0.5rem;
Mutta en tosiaan itsekään löydä noista gridiesimerkeistä samaa.
 
Mutta taisin tajuta mikä tuossa on ongelmana. Tuo kuva. Se määrittelee sivun leveyden kun se on alhaalla ja siksi teksti ei mahdu ruudulle.
 
On tuo token refresh sitten kanssa yksi saatanan työmaa. Refresh onnistuu, mutta kiljoona esimerkkiä miten sen epäonnistuneen requestin saisi tehtyä automaattisesti uudestaan ei toimi. Tulee vain "refresh.then is not a function" tästä:
Koodi:
    axios.put(addr, data, { headers: authHeader() })
        .then((response) => {
            return response.data;
        })
        .catch((err: AxiosError) => {
            if (err.response) {
                console.log("server error: " + JSON.stringify(err.response, null, 2));
            } else if (err.request) {
                console.log("no response: " + JSON.stringify(err.request, null, 2));
            } else {
                console.log("other error: " + JSON.stringify(err, null, 2));
            }
        })
Tokeni kyllä päivittyy tällä koodilla, mutta request epäonnistuu, joten lomakkeen tiedot pitää syöttää uudestaan.
Koodi:
axios.interceptors.response.use(
    (response: AxiosResponse<any>) => {
        return response;
    },
    async (error: AxiosError<any>) => {
        const originalConfig = error?.config;
        if (!originalConfig) return Promise.reject(error);
        if (error?.response?.status === 401 && error?.response?.data['msg'].includes("Token has expired")) {
            console.log("token expired");
            //const refresh: Promise<any> = await refreshAccessTokenFn();
            const res = await axios({
                method: 'post',
                url: baseURL + "refresh",
                headers: refreshHeader(),
            })
                .then(async (response) => {
                    localStorage.setItem("user", JSON.stringify(response.data));
                    console.log("token refreshed with response: " + JSON.stringify(response.data, null, 2));
                    delete originalConfig.headers.Authorization;
                    originalConfig.headers.Authorization = "Bearer " + authHeader();
                    return [null, await axios.request(originalConfig)];
                })
                .catch((error) => {
                    localStorage.removeItem("user");
                    if (error.response) {
                        console.log(error.response.data);
                    } else if (error.request) {
                        console.log(error.request);
                    } else {
                        console.log('Error', error.message);
                    }
                });
            return Promise.resolve(res);
        } else {
            return Promise.reject(error);
        }
    }
);
 
Taitanee johtua siitä, että tuon ylemmän piti olla asynkroninen, mutta sitten tuli taas uusi virhe, joka onkin vaikeampi selvittää. Nyt sitten tuossa ylemmässä funktiossa mennään tuohon else-haaraan ja mitään error-viestiä ei ole.

On kyllä hankala ongelma tutkittavaksi, kun tokeni päivittyy vaikka virhe tuleekin -> pitää aina kokeilun jälkeen odottaa että token vanhenee.

Mutta on tämä taas perkele. Välillä tuo debuggeri päättää että haluankin ajaa vanhaa versiota koodista. Eli kun olen ensin odottanut, että se tokenin perkele vanhenee, niin sen uuden version sijaan tuo W=)("# ajaa vanhan koodin, joka ei taaskaan toimi. Tässä ei tunnu olevan mitään logiikkaa.
 
Onpa kyllä kontissa kehittäminen hankalaa. Tein etusivulle pienen muutoksen ja ihan mitä tahansa yritän, niin sivun sisältö on vanha. Olen koko kompositionkin (docker-compose) ajanut alas ja takaisin ylös ja käynnistänyt Codessa koko sovelluksen uusiksi, mutta ei kerta kaikkiaan. Aloin epäilemään asiaa kun yritin muokata CSS:ää eikä mikään tuntunut vaikuttavan mihinkään.
 
Ai niin, ne kontithan pitää kääntää uudestaan. Kesän aikana tällainenkin unohtunut. WEBPACK_POLLING=true ei tunnu toimivan, eli aika hidasta on kontissa kehittäminen, kun pitää ajaa aina compose alas, rebuildata ja sitten käynnistää uudestaan.
 
Hngh. Mikä vittu tuota CORS:ia vaivaa? Vaikka en ole mihinkään siihen liittyvään käsittääkseni koskenut, eikä lokaalikehityksessä ole mitään ongelmaa, niin aina välillä kun serverin päivittää koko systeemi särkyy tuohon. Selvitäpä siinä sitten, että mikä tällä kertaa ahdistaa kun mitään tuota kummempaa tietoa ei saa. Kävin jo useamman Nginx-säädön netin ohjeiden mukaan kokeilemassa mutta eipä tunnu mikään auttavan.

Kiva säätää tällaisia perjantai-iltana. Taitaa olla hetken sivusto paskana, kun en kyllä keksi että mikä tässä mättää.
 
Laitoin sivuston esille LinkedIniin ja heti tuli pari palautetta että etusivu ei toimi mobiilissa.Tiesin sen toki ja olen pariinkin kertaan ihmetellyt, että mikä siinä on, mutta en ole tajunnut. Ongelmia on siis kaksi, kun sivua kaventaa: tekstipalsta ei kapene riittävästi ja alla oleva uutuuslista ei toimi kuten gridin luulisi toimivan, eli siirtää itemeitä seuraavalle riville kun eivät mahdu enää samalle. Epäilen, että ensimmäinen ongelma johtuu toisesta, mutta en ole tajunnut miksi.
 
Aloin sitten vihdoin miettimään automaatiotestausta, mistä seuraa tietysti testattavuuden käsite. Ja siinä törmäsin siihen, että nykyinen API on siihen huono, koska yhdellä kyselyllä haetaan kaikki sivun tiedot, eli tietorakenne on melkoinen sisäkkäisten tietorakenteiden hirviö joissakin tapauksissa.

Ajattelin sitten kokeilla yhdessä osiossa korjata tilannetta, mutta huomasin, että olin nämä ensimmäiset sivut tehnyt tässä mielessä paremmin, eli data ladataan pienemmissä paloissa. Kääntöpuolena nyt on sitten sivuja, jotka aiheuttavat jumalattoman määrän kyselyitä: https://www.sf-bibliografia.fi:3000/magazines/47.

Parannusideana tuli mieleen, että yhdellä kyselyllä haetaan kaikkien numeroiden ns. otsikot (käytännössä numero, joka voi olla juokseva tai esimerkin mukainen, plus mahdollinen alaotsikko). Sitten teen komponentin, jossa otsikko on foldattu (onko tuolle jotain suomenkielistä termiä?) ja kun sen avaa, niin ladataan ko. numeron tiedot. Huonona puolena tietysti sivulta ei voi hakea mitään, ellei tuohon lehden nimen alle lisää jotain hakuhimmeliä. Toisaalta, en tiedä tarvitseeko joku edes sellaista. Virtuaaliskrolleri olisi tietysti yksi mahdollisuus, mutta itse vihaan sellaisia sivuja. Odotan mieluummin että sivu ladataan ja selaan sitä sitten kuin selailen pikku hiljaa alaspäin ja aina odotan, että seuraava pätkä ladataan.

Iso ajatus olisi kuitenkin se, että API-kutsussa ei olisi sisäkkäisiä tietorakenteita, siis esimerkiksi teoksen tietoja hakiessa ei tule painos-, tekijä- ym. tiedot mukana, vaan ne haetaan erikseen. Tässä vaan on parissa kohtaa sama ongelma kuin yllä, esimerkiksi kirjoja hakiessa saattaa tulosjoukossa oli satoja teoksia, niille löytyy sitten jokaiselle vähintään yksi painos. Vai pitäisiköhän tuolle vaan tehdä poikkeus?

Löysin muuten vihdoin sopivan työkalun API-testaamisen automatisointiin, httpYac: httpYac | httpYac. Simppeli, mutta silti mahdollistaa monenlaista.

Viime aikoina alkanut sivujen lataaminen kestää unpkg:n takia. Mikähän siinäkin on?
 
Ja sivumennen mainittuna alkanut mietityttämään, että pitääköhän jossain vaiheessa miettiä stackia uusiksi. Olen siis käyttänyt create-react-appia, joka on deprekoitu. Sen sijaan suositellaan esimerkiksi Next.js:ää, mutta mitä kokeilin sitä syksyllä pikaisesti, niin aika paljon vaatisi tunkkaamista. SSR toki kiinnostaisi, mutta vaatisi vielä lisää tunkkaamista.

En myöskään erityisesti pidä Flaskista/Pythonista bäkkärinä.
 
Django nyt olisi lähinnä askel taaksepäin. Enemmänkin joko joku TypeScript-pohjainen, jolloin voisi käyttää samoja Zod-malleja tai sitten opiskelumielessä Rocket tai joku muu Rust-pohjainen. Tai sitten Nextjs:ssä koko höskä Prisman avulla.
 
Nextjs ja trpc kävisi varmaan jos zodia käytät jo frontissa. Jos ei käytä noita react server comppnentteja niin ei kai nextjs ole hirveän iso työmaa opetella.
 
No, kuten sanoin, tulihan tuota kokeiltua yhdessä hommassa aluksi, mutta kun jo tyhjältä pöydältä aloitetussa projektissa joutui tuskailemaan hydraatio-ongelmien ja server/client-juttujen kanssa, niin olemassaolevan koodin siirtäminen vaikuttaa kinkkiseltä. Esimerkiksi jos sivulla on mitään interaktiivista, niin se pitäisi määritellä 'use client', mutta tuossakin tuli ongelmia itselläni. Onhan ne ratkottavissa, mutta en usko, että ihan triviaali homma. Mutta jossain vaiheessa tuo siirtymä lie pakko tehdä kun CRA ollut poistumassa jo hyvän tovin eikä sitä enää ylläpidetä.

tRPC vaikuttaa kiinnostavalta, olin Theolta tuosta joskus kuullutkin, mutta olin jo unohtanut.
 
Jos nykyisellä stackillä jatkat, niin mikset siirry vain cra → vite?
 
En ole perehtynyt, että millainen homma tuo olisi ja miten tuo eroaa Nextistä. Ja SSR kuitenkin kiinnostaa, ja se toimii ilmeisesti Nextissä paremmin.
 
Lukaisin nopeasti Nextin renderöintivaihtoehdoista, ja tämä olisi aika soppeli omaan käyttööni: Data Fetching: Incremental Static Regeneration (ISR) | Next.js. Sivuja alkaa olla kuitenkin kohta 10.000, päivityksiä tapahtuu määrään nähden harvakseltaan. Eli jos vain saan tehtyä sivuista muuten SSG:t, niin toimintahan paranisi ihan älyttömästi kun sivut latautuisivat silmänräpäyksessä,
 
Ajattelin sitten kokeilla NextJS:ää. Etusivun siirto ja sen generointi SSR:llä ei ollut kovin kummoinen homma (toki tyylit ym puuttuu vielä), mutta sitten kävin ahneeksi ja ajattelin lisätä seuraavaksi tRPC:n ja Drizzlen.

Saattaa olla, että tRPC on tällä hetkellä tekemätön paikka, kun ohjeita NextJS:n kanssa toimimiseen ei ole hetkeen päivitetty. Sain ehkä tunkattua tuon siltä osin kuntoon, että kun ohjeet on vanhalle PageRouterille ja NextJS:ssähän käytetään nykyään AppRouteria, niin tuo kuitenkin yrittää lähteä käyntiin. Mutta jostain syystä, vaikka olen enabloinut tRPC:ssä SSR:n niin softa silti kaatuu käynnistyksessä ja valittaa, että jostain tRPC:n komponentista puuttuu 'use client':

Koodi:
Error: useState only works in Client Components. Add the "use client" directive at the top of the file to use it. Read more: https://nextjs.org/docs/messages/react-client-hook-in-server-component

Call Stack
WithTRPC
(rsc)/node_modules/@trpc/next/dist/withTRPC.mjs (12:43)

Pitänee kokeilla vielä noita Server-Side Helpereitä, jotka ohjeiden mukaan mahdollistavat SSR:n ja SSG:n ilman että tRPC on siinä moodissa.
 

Statistiikka

Viestiketjuista
261 795
Viestejä
4 547 434
Jäsenet
74 849
Uusin jäsen
ookooo

Hinta.fi

Back
Ylös Bottom