(⌐■_■)

Det er noen besvergelser for å få en .sol kontrakt klar for deploy i en Go-app. Man kan klone git-repoet på remix.ethereum.org og kompilere kontrakten der, men jeg må nesten skjønne hvordan jeg skal klare å få deployet den på blokkjeden og kalt den i appen. Denne ressursen har en step-by-step: https://goethereumbook.org/smart-contract-compile/

Du begynner med en .sol fil, jeg fikk chatGPT til å lage en kontrakt som kan mappe en id til et versjonsnummer, hente versjonsnummeret til en id, og emitte et event når en id blir oppdatert med ny versjon:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

contract VersionControl {
    // Event emitted whenever an item's version is updated
    event VersionUpdated(bytes32 indexed itemId, uint256 newVersion);

    // Mapping of item IDs to their current version number
    mapping(bytes32 => uint256) public itemVersions;

    // Function to update the version of an item
    function updateVersion(bytes32 itemId, uint256 newVersion) public {
        // Here, you could add access control checks
        
        // Update the item's version
        itemVersions[itemId] = newVersion;

        // Emit an event indicating the version update
        emit VersionUpdated(itemId, newVersion);
    }

    // Function to get the current version of an item
    // This function is view-only and does not incur gas fees
    function getVersion(bytes32 itemId) public view returns (uint256) {
        return itemVersions[itemId];
    }
}

Fila heter hashHandler.sol.

Målet vårt er to ting: 1) Generere .go som lar oss kalle metodene i kontrakten fra appen, og 2) Kompilere kontrakten til “EVM bytecode” som skal i data-feltet på transaksjonen når vi deployer den til blokkjeden.

Vi trenger to verktøy for å få til dette, solc og abigen. Begge er laget for Linux, så hvis du er på Windows er det aller enkleste å deale med dem i WSL.

Filene: hashHandler.sol ➡️ hashHandler.abi ➡️ hashHandler.go

Du bruker solc for å generere en ABI (“application binary interface”) fra kontrakten. ABI-en bare en JSON. Hvis du ser på nærmere byggrasket i Remix baker de ABI’en inn i en JSON. Hos oss skal den ha extension .abi.

Gå inn i mappa der kontrakten ligger og si:

> solc --abi hashHandler.sol

Det outputtes en JSON-struktur i konsollet. Kopier den, lag ny fil hashHandler.abi (eller tilsvarende din kontrakt) og lim inn. Fra denne får vi generert .go-en:

> abigen --abi=hashHandler.abi --pkg=hashhandler --out=hashHandler.go

Men uten deploy methods, for vi har ikke lagt til EVM bytecoden. Generer den med:

> solc --bin hashHandler.sol

Det outputtes binary som du kan lime inn i ny fil hashHandler.bin. Da kan du si:

> abigen --bin=hashHandler.bin --abi=hashHandler.abi --pkg=hashhandler --out=hashHandler.go

Vi har nå fått en programfil fra .sol kontrakten som kan deployes og kalles.

Git

Vil bare logge at Github krever autentisering hvis du skal klone private prosjekter, og det kan løses med SSH-nøkler, men hvis du har flere kontoer krever Github at hver konto har sin egen nøkkel, og den private delen av nøkkelen må registreres lokalt på PCen, så hvis du har flere må de skilles fra hverandre i en egen config-fil. Så når du kloner ned må du spesifisere aliaset fra configen.

Lage nøkkel

Du lager et privat/public RSA-nøkkelpar (to filer) i Administrator: Powershell. De skal ligge i mappa .ssh, som skal ligge rett på useren din i Windows:

> cd C:\Users\ingri\.ssh
> ssh-keygen -t rsa -b 4096 -C "din@epostadresse.com"

Du blir bedt om å gi nøkkelen et navn; den private fila blir da stående uten extension: navn og den public med: navn.pub.

Enable SSH-agenten

Sjekk at SSH-agenten er enablet (hos meg var den ikke). Du bruker den til å registrere den private nøkkelen lokalt på PCen:

> Get-Service ssh-agent | Select-Object -Property StartType

Hvis det kommer opp at den er disablet, kjør:

> Set-Service -Name ssh-agent -StartupType Automatic

Registrere privat nøkkel

Du kan nå legge til den private nøkkelen:

> ssh-add ~\.ssh\navn

Kopiere public nøkkel

Hvis det funket skal du nå kopiere innholdet i den public-e nøkkelen:

> Get-Content ~\.ssh\ingridskard.pub

Kopier hele outputten, den starter med ssh-rsa og slutter med epostadressen du oppga.

Legge til public nøkkel på Github

Gå til Github-kontoen din, Settings, SSH And GPG Keys. Opprett ny SSH-nøkkel, type “Authentication Key”, lim inn outputten og lagre.

Lokal konfig ved flere nøkler

Dette vil nå funke ut av boksen hvis du holder på med bare én Github-konto. Men hvis på flere kontoer kan SSH-nøklene snuble i hverandre lokalt. Da kan du opprette en fil config i .ssh-mappa der du aliaser nøklene, så systemet klare å holde dem fra hverandre:

# Default GitHub account (ingridskard)
Host github.com-ingridskard
  HostName github.com
  User git
  IdentityFile ~/.ssh/ingridskard
  IdentitiesOnly yes

# Another GitHub account
Host github.com-shinysticker
  HostName github.com
  User git
  IdentityFile ~/.ssh/shinysticker
  IdentitiesOnly yes

Bruk av alias ved kloning

Når du kloner repo da må du bruke aliaset:

$ git clone git@github.com-ingridskard:ingridskard/ever-ip.git

Det som skjer nå er at Git sender med public-nøkkelen når du prøver å klone, og hvis systemet klarer å finne referanse til riktig privat-nøkkel i konfigen og den matcher, får du fortsette. Hvis nøklene ikke matcher får du feilmelding:

git@github.com: Permission denied (publickey).
fatal: Could not read from remote repository.

Hvis det ikke er satt opp noen nøkkel eller systemet ikke finner den, får du feilmelding om at repoet ikke fins.

Første gang systemet autentiserer med ny nøkkel spør git:

The authenticity of host 'github.com (140.82.121.4)' can't be established.
ED25519 key fingerprint is SHA256:+DiY3wvvV6TuJJhbpZisF/zLDA0zPMSvHdkr4UvCOqU.
This key is not known by any other names.
Are you sure you want to continue connecting (yes/no/[fingerprint])? 

Her skal du bare svare ja.

Strafferunde: Få VS Code til å lese aliasene

Du må sette aliaset på remote-url i repoet ditt for at VS Code skal klare å lese det. Du kan se hva VS Code leser nå med

> git remote -v

Hvis den urlen uten alias (typ git@github.com:ingridskard/repoet-ditt.git) kan du oppdatere den med:

> git remote set-url origin git@github.com-ingridskard:ingridskard/repoet-ditt.git

🔥

Bare litt om det frontend Auth dramaet. Kall det sunken cost fallacy, bruke to dager på å få opp frontend Auth når det ikke står på klientens liste, bare for å se om det er fort gjort å få opp eksempelet – for så å krasje appen slik at en dag ble å finne ut at [csrf] settingen gjør så cookien ikke blir slettet, som antagelig har å gjøre med at user ble undefined i requesten av og til.

Da følte jeg meg litt dum. Men her er det jeg ser nå. Det er en liste med ting som ikke bare Siv skal ha, alle skal ha det: Brukerinnlogging, theme, betaling med betalingsløsning for ting, reservering av plass, sending av epost til gruppe, gavekort, klippekort, blogg, nyhetsbrev, og sider der du kan redigere i teksten, bytte ut og legge til bilder…

Jeg tror det vil koste mer på sikt å ikke bygge funksjonene inn under brukerinnlogging nå. Det gir mer komplette data. Siv trenger det kanskje ikke nå men de aller fleste bedrifter som selger ting på nettet skal ha det, for både datakompletthet og rapporter og egen presentasjon. Litt flere klikk, men innlogging oppleves tryggere for kunder.

Det sitter liksom langt inne men jeg fant i går at Siv trenger brukerinnlogging for å få til klippekort (og gavekort) uansett. Siden hennes blir bedre med brukerinnlogging. Det er viktig funksjonalitet som har med kundenes datasikkerhet å gjøre. Så det er fornuftig å innarbeide det tidlig for min egen kompetanse sin del.

(˘・_・˘)

På tide å reprioritere etter to uker på Gran Canaria.

Intimsonen: Design kom delvis. Jobben må legges ut på nytt og gjøres ferdig av en annen designer. Det tar 1-2 uker. Skal implementeres, kontrakt utformes, fakturasystem være oppe. Utforming kontrakt og oppretting fakturasystem er bare gjørejobb. Siten trenger at epost fungerer, adminbranding.

  1. Fiks tenantlogin
  2. Epost for forms
  3. Admin datafilter virker

OFP: Design på vei. MVP skal ha foredragsliste, påmelding, rapport, epostklient. Krever alt over pluss:

  1. Ferdig epostklient
  2. Media

Overordnet skal vi ha:

  1. Unittester
  2. Adminbranding

Trenger vi egentlig?:

  1. Frontend-for-backend auth for:
  2. Edit i frontend

(▀̿Ĺ̯▀̿ ̿)

Module resolution med node

Det tok tre dager å få til bygg og serve av backend igjen, og det har lært meg følgende facts:

  • Som utvikler setter man path aliases for å unnslippe path hell – relative paths som må endres manuelt overalt hvis du flytter på ting (i tillegg til at lange relative paths er stygge å se på.) Vi vil ha import { foo } from 'bar/foo', ikke import { foo } from '../../../../../bar/foo'.
  • Du legger til path aliases i tsconfig.json for å få intellisense på de pathene.
  • Hvis du setter path aliases i tsconfig.json må du samtidig sette baseUrl, ellers funker det ikke. Du skal også sette moduleResolution til node.
  • Byggescript, f. eks. next build resolver path aliases ut av boksen, men hvis du bruker bundler må path aliasene legges til bundler-configens resolve.alias for at bundleren skal klare å resolve dem.
  • Vi kjører appen i Node.js, som er et runtime environment for Javascript. Det første node gjør ved oppstart er å resolve alle imports og exports. Da er det slik at node ikke har begrep om path aliases. Den må ha relative paths. Så hvis du bygger og bundler med Webpack og så prøver å serve med node, så knekker det med vår venn, MODULE_NOT_FOUND.
  • Det fins ulike måter å løse det på, blant annet kan du kjøre noe som heter tsc-alias rett etter tsc som leser path aliases fra tsconfig.json, går gjennom alle modulene dine og erstatter path aliases med relative paths i Javascript-outputen.
  • Har ikke sett IDE bemerke det, men “trailing commas” er ikke støttet i JSON. Kan føre til at verktøy som tsc-alias feiler ved parsing av tsconfig.json.

ᓚᘏᗢ

Edit: Da blir det klonede instanser av én rigg med blandede tenants. Første milepæl er publisering av kampanje. Før det skal tre sites opp: OF Promo, Siv Nancy, og Firstbird Promo til portfolio.

Lurte på om jeg skal ha bare én rigg som jeg utvikler i ett og bare ett gitrepo. Så blir multitenant-0, multitenant-1 og multitenant-2 instanser av den med ulike bruksområder. Da har alle instanser alle collections, endpoints, plugins osv. men de bruker ulike partisjoner.

Kan jeg slippe unna med å deploye alt fra ett og samme repo? Si de ligger på egne branches som trigger deploys, så utvikler jeg på main og merger inn fortløpende. Da blir det mulig å holde alle up-to-date.

Men hvilken gevinst har det å partisjonere instansene da, kan jeg ikke bare bygge ut riggen og plusse på tenants etter hvert som de dukker opp? Mer fleksibilitet til å utvikle riggen etter behov?

Tanken var at poenget med multitenancy var å la tenants dele funksjonalitet og også unngå å tilpasse adminpanelet for dem. Men lignende tenants vil fortsatt ha ulike behov til adminpanelet, så den jobben må gjøres uansett. Da opprettes nye instanser etter hvert som de gamle begynner å fylle seg opp med data. Og det blir lett å utvide tenants med flere features.

Angående multitenant-0 så var den tenkt uten adminpanel eller noe funksjonalitet, kun enkle “visittkort på nett”. Men da trenger jeg bare en Next app og ingen backend.

Da gjenstår bare å se hvilke features skal på plass først.

Kan man gruppere features etter vanlige usecases? Typ:

Multitenant-0: Visittkort-på-nett

Adminpanel: Nei
Auth: Nei
Features: Kontaktskjema
Pris: 15

Disse er ikke tenants men Next-apper på kun Vercel.

Usecases er bedrifter som trenger et SEO-optimalisert webnærvær der det informeres om tjenestene de tilbyr, hvorfor du bør velge dem og hvordan man kommer i kontakt. For SEO skal de være SSG og optimalisert for SEO for øvrig, kanskje inkludert forvaltning av Google Bedrift-oppføring, hjelp med innsamling av reviews osv. Prototyper fra ekstern designer.

Multitenant-X: Auth uten adminpanel

Adminpanel: Nei
Auth: Ja (inkl. Payload)
Features: Edit-in-place, Auth
Pris: Ca. 20

Spesielt setup for edit-in-place uten adminpanel.

Usecase er websider med innhold som endrer seg eller oppdaterer seg ofte, men strukturen (sider, layout) aldri endrer seg, f. eks. bedrifter som legger til eller endrer tjenestene sine ofte.

Redigerbart innhold bør være variert for å fange maks nisje-usecases: Richtekst-editor til HTML (vanlig tekst med utvidet støtte), bilde og bildegalleri eller -karusell, accordion, kodeblokk, sitatblokk, osv. Det er kult fordi implisitt kommer alltid editerbare strukturer inkl. innhold i sidens design. (Selve editmodus kan være superenkelt.)

Typer redigerbart innhold kan være Payload Blocks som lages støtte for i frontend. Merk at admin må kunne legge til og fjerne blocks i frontend.

Det blir som en WordPress-side for basic behov, men med bedre brukbarhet og design.

Ved login blir admin redirectet til frontend. De er logget inn i Payload, uten adminpanel men kan nå åpne innhold for redigering i frontend. Endringene blir committet til collections i Payload.

Vil unngå en første-iterasjon der admin må logge inn i adminpanelet for å oppdatere innhold der, det er dårlig brukbarhet.

Krever ikke admin-branding siden adminpanelet ikke er i bruk, men edit-in-place krever at Auth er implementert, og det krever mye utvikling av frontend for å støtte oppretting, endring og fjerning av ulike typer blocks og committing av ulike typer endringer til Payload.

Multitenant-1

1 collection, kun standardfeatures.

Du kan logge inn og oppdatere én toppcollection (f. eks. Events). Du får en frontend med et tilpasset view av dataene (f. eks. en liste over foredrag, konsertplan, turneplan).

Login: Ja, med adminpanel
Collections: 1 (inkl. view)
Features: Ingen tilpassede features
Pris: Ca. 25

Multitenant-2

Collections med booking (inkl. betaling) og aktivitetsrapporter.

Du kan logge inn og oppdatere flere collections (f. eks. Courses og Events). Du får visning av dataene i frontend, inkl. funksjonalitet for påmelding og betaling. Du får aktivitetsrapporter i adminpanelet.

Login: Ja, med adminpanel
Features: Påmelding/betaling, aktivitetsrapporter
Pris: Ca. 35

Tilpassede features

  • Booking: “Å kjøpe plass på et arrangement. Når arrangementet er over blir plassen borte. For å kjøpe ny plass må nytt arrangement opprettes
  • Reservation: “Å reservere en ressurs i et tidsrom. Etter tidsrommet blir ressursen tilgjengelig for reservering igjen.
  • Contract: Arbeidsflyt for utarbeidelse av avtaletekst med maltekster og -variabler, signatur med “magic link” el, PDF på epost…
  • Payment: Integrering med API for betaling med Vipps, Paypal, Stripe, krypto mm, registrering av påmelding…
  • Activity Report: Tabellvisning av historikk/aktivitet i forbindelse med booking, reservation mm, inkl. mulighet for eksport til fil eller Google Drive…

Standardfeatures

Ikke-tilpassede features som bør tilbys på alle produkter:

  • Kontaktskjema
  • Nyhetsbrev
  • Blogg
  • Admin: Branding
  • Admin: Pluss-add

Collections

Collections er samlinger av dokumenter. De har en viss shape gitt ved et konfigurert skjema i Payload. Features kan interface trygt med dem via typer.

Toppcollection er collections der andre, ikke-trivielle collections er nøstet inn. F. eks. et dokument av Courses gir typisk ikke mening hvis den mangler en liste med dokumenter av Events (mens en collection Addresses for å angi lokasjon er heller triviell.)

(✿◡‿◡)

Git

Jeg har jo et monorepo med ett backend-prosjekt som server n frontend-prosjekter. Så en vanlig workflow for meg er å kjøre backendprosjektet på localhost samtidig som jeg kjører ett av frontendprosjektene på localhost.

Samtidig er backenden deployet på Payload Cloud med bygg-trigger på innsjekk til en egen branch payload-cms, og frontendene deployet på Vercel med bygg-trigger på deres respektive branches. Men,

  • Det er klønete å jobbe mot to deploy-branches i ett repo fordi alle endringer må sjekkes inn til riktig branch hele tiden, hvis jeg sjekker inn endringer på prosjekt A til prosjekt B sin branch går As deploy glipp av dem og man må merge branchene manuelt.
  • Fordi branchene typisk ikke er i sync med hverandre kan jeg ikke ha branch A sjekket ut lokalt og kjøre branch B sin app i siste versjon. Jeg gidder ikke å merge branchene med hverandre (og med main) kontinuerlig.
  • Jeg kan ikke åpne repoet to ganger i VS Code; git endrer jo selve filsystemet etter utsjekket branch, så hvis jeg bytter branch i den ene instansen av VS Code så reflekteres det bare i den andre instansen.
  • Jeg kan ikke droppe branches og jobbe mot kun main, for da trigger jeg bygg på alle prosjektene hver gang jeg sjekker inn.
  • Jeg kan klone repoet to ganger og sjekke ut en branch i hver, men det fins bedre måter.

Arbeidsflyt med working trees

Man kan sjekke ut egne working trees fra repoet, ett for branch A og ett for branch B: https://andrewlock.net/working-on-two-git-branches-at-once-with-git-worktree/

Working trees er bare kopier av hele repoet, så det er som å ha klonet det to ganger, men i stedet for egne git-mapper har du en git-fil som peker på et rot-repo, så working-trærne jobber mot ett og samme filsystem; egne kloner er egne filsystemer.

Man kan da ta opp hver branch i egen instans av VS Code og kjøre appene i nyeste versjon der. Hvert innsjekk vil trigge bygg.

Kommando for å ta ut nytt working tree project-a fra branch-a:

git worktree add ../project-a branch-a

Trunk-based arbeidsflyt

Et annet alternativ er å jobbe rett på main. Ingen av prosjektene trigger der, så jeg kan sjekke inn kode fortløpende og merge til de respektive branchene når de er klare. Jeg trenger ikke tenke på at endringer i A må sjekkes inn ett sted og B et annet sted.

Ved større endringer kan jeg ta ut en arbeidsbranch, gjøre endringene der, merge til main og merge til trigger-branchen når alt er klart. Hvis flere utviklere skal jobbe mot samme repo kan main (og trigger-branchene) beskyttes med PR.

main er alltid nyest, så prosjektene kan kjøre parallelt på localhost i siste versjon. Pluss branchene blir alltid merget med siste nytt fra alle andre branches – som ikke har noen praktisk betydning, tror jeg, men jeg foretrekker det.

Jeg kan derfor jobbe mot alle prosjektene i samme instans av VS Code. Ved store endringer der jeg foretrekker å ha A og B i egne instanser av VS Code kan jeg lage en ny klon av repoet. Eventuelt holde meg i samme klon med egen arbeidsbranch i eget working tree. Det går ikke å ta ut samme branch to ganger i parallelle working trees, derav behovet for egen arbeidsbranch i så fall.

Kommando for å merge nyeste lokale commit (HEAD) med branch-a (husk å stå i main):

git push origin HEAD:branch-a

🪔

Vipps

På tide å bli kjent med Vipps sine APIer. De er jo “Vipps MobilePay” nå. Mannen til Eva snakket om det på NDC, han jobber i Vipps. De kjøpte opp MobilePay for et år siden, finner jeg på nettsidene deres. Dette handlet til dels om å få integrert med CBDCene som er på vei. Men det er fint, da er Vipps på topp og det er bare å integrere med dem.

Ser de har såkalt “partnerprogram” for utviklere som lager løsninger på vegne av merchants. Da får jeg én API-nøkkel fra Vipps, en “partnernøkkel”, som lar bruke et eget “Management API” der jeg kan integrere Vipps på merchants’ vegne uten å måtte logge inn på bedriftsportalene deres. Regner med at merchants da inngår avtale med Vipps om samarbeidet med meg, så blir jeg tildelt tilgangene jeg trenger.

Men jeg trenger noen API-nøkler for å få testet integrasjonen. Det var mulig å lage konto med BankId og et gammelt enkeltpersonforetak og opprette test-salgssted, der var det nøkler.

Forresten så er visst partnernøkler kun for “Partner Pluss” og så videre, hvis du har 30+ merchants eller over ganske mye i omsetning. Innen den tid er det mulig for merchants å gi meg brukertilgang i portalen sin: https://developer.vippsmobilepay.com/docs/partner/add-portal-user/

Når man er “Platform partner” (som er typen partner som passer for meg) skal merchants onboardes slik: https://developer.vippsmobilepay.com/docs/partner/#how-to-sign-up-new-merchants

Best practices: Vipps anbefaler å sjekke return-responsen fra hvert API-kall. I tillegg anbefales det å benytte seg av API-dashboardet på vippsportalen: https://developer.vippsmobilepay.com/docs/developer-resources/api-dashboard/

Wow, implementere Vipps i backenden etter hvert?: https://developer.vippsmobilepay.com/docs/SDKs/node-sdk/

Eksterne script i Next

Man kan inkludere Scripts fra next/scripts for å legge til eksterne scripts med src. Det legger seg i header, eller man kan sette parametre som bestemmer hvor det legger seg.

Man kan da kalle elementer i scriptet, men Typescript vil klage på de ukjente typene. Så man må legge til egne typespesifikasjoner som Typescript leser i henhold til include-direktivet i tsconfig.json.

Jeg laget en ny mappe “/types” med en “custom-elements.d.ts”:

declare namespace JSX {
    interface IntrinsicElements {
      [elemName: string]: any;
    }
  }

Da tillater Typescript alle custom-elementer, typ <vipps-checkout-button variant="orange" branded="false" /> fra Vipps.

Man kunne vært mer spesifikk og bevart typesikkerhet på alle andre custom-elementer:

declare namespace JSX {
  interface IntrinsicElements {
    'vipps-checkout-button': React.DetailedHTMLProps<React.HTMLAttributes<HTMLElement>, HTMLElement> & {
      variant?: string;
      branded?: boolean;
    };
  }
}