diff --git a/.github/workflows/taco-release.yml b/.github/workflows/taco-release.yml index ca16bc874..da73cf120 100644 --- a/.github/workflows/taco-release.yml +++ b/.github/workflows/taco-release.yml @@ -5,6 +5,7 @@ on: tags: - 'taco/cli/v*' - 'taco/statesman/v*' + - 'taco/token-service/v*' permissions: contents: write @@ -73,6 +74,8 @@ jobs: echo "build_dir=taco/cmd/taco" >> $GITHUB_OUTPUT elif [ "$APP_DIR" = "taco/statesman" ]; then echo "build_dir=taco/cmd/statesman" >> $GITHUB_OUTPUT + elif [ "$APP_DIR" = "taco/token-service" ]; then + echo "build_dir=taco/cmd/token_service" >> $GITHUB_OUTPUT fi - name: Build CLI @@ -93,6 +96,15 @@ jobs: -o statesman-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }} . sha256sum statesman-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }} > statesman-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }}.sha256 + - name: Build Token Service + if: startsWith(steps.meta.outputs.app_dir, 'taco/token-service') + working-directory: ${{ steps.meta.outputs.build_dir }} + run: | + CGO_ENABLED=0 GOOS=${{ matrix.goos }} GOARCH=${{ matrix.goarch }} go build \ + -ldflags="-X 'main.Version=${{ steps.meta.outputs.version }}' -X 'main.Commit=${{ github.sha }}' -s -w" \ + -o token_service-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }} . + sha256sum token_service-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }} > token_service-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }}.sha256 + - name: Upload CLI artifacts if: startsWith(steps.meta.outputs.app_dir, 'taco/cli') uses: actions/upload-artifact@v4 @@ -107,8 +119,15 @@ jobs: name: taco-statesman-${{ matrix.os }}-${{ matrix.arch }} path: ${{ steps.meta.outputs.build_dir }}/statesman-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }}* + - name: Upload Token Service artifacts + if: startsWith(steps.meta.outputs.app_dir, 'taco/token-service') + uses: actions/upload-artifact@v4 + with: + name: taco-token-service-${{ matrix.os }}-${{ matrix.arch }} + path: ${{ steps.meta.outputs.build_dir }}/token_service-${{ matrix.os }}-${{ matrix.arch }}${{ matrix.goos == 'windows' && '.exe' || '' }}* + build-docker: - if: startsWith(github.ref_name, 'taco/statesman/') + if: startsWith(github.ref_name, 'taco/statesman/') || startsWith(github.ref_name, 'taco/token-service/') runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -136,11 +155,22 @@ jobs: username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} + - name: Determine service name + id: service + run: | + if [[ "${{ github.ref_name }}" == taco/statesman/* ]]; then + echo "name=statesman" >> $GITHUB_OUTPUT + echo "dockerfile=Dockerfile_statesman" >> $GITHUB_OUTPUT + elif [[ "${{ github.ref_name }}" == taco/token-service/* ]]; then + echo "name=token-service" >> $GITHUB_OUTPUT + echo "dockerfile=Dockerfile_token_service" >> $GITHUB_OUTPUT + fi + - name: Extract metadata id: docker-meta uses: docker/metadata-action@v5 with: - images: ghcr.io/${{ github.repository }}/taco-statesman + images: ghcr.io/${{ github.repository }}/taco-${{ steps.service.outputs.name }} tags: | type=raw,value=${{ steps.meta.outputs.version }} type=ref,event=tag @@ -150,7 +180,7 @@ jobs: uses: docker/build-push-action@v5 with: context: ./taco - file: ./taco/Dockerfile_statesman + file: ./taco/${{ steps.service.outputs.dockerfile }} push: true platforms: linux/amd64,linux/arm64 tags: ${{ steps.docker-meta.outputs.tags }} @@ -261,3 +291,57 @@ jobs: taco-statesman-*/* draft: false prerelease: false + + create-release-token-service: + if: startsWith(github.ref_name, 'taco/token-service/') + needs: [build, build-docker] + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Derive app dir and version + id: meta + run: | + TAG="${GITHUB_REF_NAME}" # e.g. taco/token-service/v1.2.3 + APP_DIR="${TAG%/v*}" # taco/token-service + VERSION="${TAG##*/}" # v1.2.3 + echo "app_dir=$APP_DIR" >> $GITHUB_OUTPUT + echo "version=$VERSION" >> $GITHUB_OUTPUT + + - name: Download all artifacts + uses: actions/download-artifact@v4 + + - name: Create release for Token Service + uses: softprops/action-gh-release@v1 + with: + tag_name: ${{ github.ref_name }} + name: taco/token-service/${{ steps.meta.outputs.version }} + body: | + ## Taco Token Service ${{ steps.meta.outputs.version }} + + ### Downloads + - **Linux AMD64**: [token_service-linux-amd64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-linux-amd64) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-linux-amd64.sha256)) + - **Linux ARM64**: [token_service-linux-arm64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-linux-arm64) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-linux-arm64.sha256)) + - **Linux 386**: [token_service-linux-386](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-linux-386) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-linux-386.sha256)) + - **macOS AMD64**: [token_service-darwin-amd64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-darwin-amd64) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-darwin-amd64.sha256)) + - **macOS ARM64**: [token_service-darwin-arm64](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-darwin-arm64) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-darwin-arm64.sha256)) + - **Windows AMD64**: [token_service-windows-amd64.exe](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-windows-amd64.exe) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-windows-amd64.exe.sha256)) + - **Windows 386**: [token_service-windows-386.exe](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-windows-386.exe) ([checksum](https://github.com/${{ github.repository }}/releases/download/${{ github.ref_name }}/token_service-windows-386.exe.sha256)) + + ### Docker + ```bash + docker pull ghcr.io/${{ github.repository }}/taco-token-service:${{ steps.meta.outputs.version }} + ``` + + ### Installation + Download the appropriate binary for your platform and make it executable: + ```bash + chmod +x token_service-- + sudo mv token_service-- /usr/local/bin/token_service + ``` + files: | + taco-token-service-*/* + draft: false + prerelease: false diff --git a/helm-charts/opentaco/Chart.yaml b/helm-charts/opentaco/Chart.yaml index c0d189884..951dc6d94 100644 --- a/helm-charts/opentaco/Chart.yaml +++ b/helm-charts/opentaco/Chart.yaml @@ -10,6 +10,7 @@ appVersion: "0.1.0" # - PostgreSQL database (optional - can use Cloud SQL instead) # - Digger Managed (terraform orchestration backend) # - Taco Statesman (IaC state management) +# - Token Service (API token management) # - Drift Detection service # - Taco UI (React frontend) @@ -22,48 +23,42 @@ dependencies: tags: - database - # Digger Managed - terraform orchestration backend + # Taco Orchestrator - terraform orchestration backend - name: taco-orchestrator version: "0.1.0" - # For production: use OCI registry repository: "oci://ghcr.io/diggerhq/helm-charts" - # For local testing: use file reference - #repository: "file://../taco-orchestrator" condition: taco-orchestrator.enabled tags: - backend # Taco Statesman - IaC state management - - name: statesman - alias: taco-statesman + - name: taco-statesman version: "0.1.0" - # For production: use OCI registry repository: "oci://ghcr.io/diggerhq/helm-charts" - # For local testing: use file reference - #repository: "file://../taco-statesman" condition: taco-statesman.enabled tags: - backend + # Token Service - API token management + - name: taco-token-service + version: "0.1.0" + repository: "oci://ghcr.io/diggerhq/helm-charts" + condition: taco-token-service.enabled + tags: + - backend + # Drift Detection - - name: drift + - name: taco-drift version: "0.1.0" - # For production: use OCI registry repository: "oci://ghcr.io/diggerhq/helm-charts" - # For local testing: use file reference - #repository: "file://../taco-drift" - condition: drift.enabled + condition: taco-drift.enabled tags: - backend # Taco UI - React frontend - - name: ui - alias: taco-ui + - name: taco-ui version: "0.1.0" - # For production: use OCI registry repository: "oci://ghcr.io/diggerhq/helm-charts" - # For local testing: use file reference - #repository: "file://../taco-ui" condition: taco-ui.enabled tags: - frontend diff --git a/helm-charts/opentaco/templates/NOTES.txt b/helm-charts/opentaco/templates/NOTES.txt index aceb38db8..fd1018153 100644 --- a/helm-charts/opentaco/templates/NOTES.txt +++ b/helm-charts/opentaco/templates/NOTES.txt @@ -21,8 +21,8 @@ DEPLOYMENT STATUS: {{- end }} {{- if index .Values "taco-orchestrator" "enabled" }} -✓ Digger Managed: Enabled - Service: digger-managed:3000 +✓ Taco Orchestrator: Enabled + Service: taco-orchestrator:3000 {{- end }} {{- if index .Values "taco-statesman" "enabled" }} @@ -30,9 +30,14 @@ DEPLOYMENT STATUS: Service: taco-statesman:8080 {{- end }} -{{- if .Values.drift.enabled }} +{{- if index .Values "taco-token-service" "enabled" }} +✓ Token Service: Enabled + Service: taco-token-service:8081 +{{- end }} + +{{- if index .Values "taco-drift" "enabled" }} ✓ Drift Detection: Enabled - Service: drift:3004 + Service: taco-drift:3004 {{- end }} {{- if index .Values "taco-ui" "enabled" }} diff --git a/helm-charts/opentaco/values-production.yaml.example b/helm-charts/opentaco/values-production.yaml.example index 43fc20510..28f5167d9 100644 --- a/helm-charts/opentaco/values-production.yaml.example +++ b/helm-charts/opentaco/values-production.yaml.example @@ -18,9 +18,9 @@ cloudSql: serviceAccount: "cloudsql-sa" # ============================================================================ -# Digger Managed +# Taco Orchestrator # ============================================================================ -digger-managed: +taco-orchestrator: enabled: true digger: replicaCount: 2 @@ -39,7 +39,7 @@ digger-managed: host: "api.opentaco.example.com" # CHANGE THIS path: / tls: - secretName: digger-managed-tls + secretName: taco-orchestrator-tls # ============================================================================ # Taco Statesman @@ -64,7 +64,7 @@ taco-statesman: # ============================================================================ # Drift Detection # ============================================================================ -drift: +taco-drift: enabled: true drift: replicaCount: 2 diff --git a/helm-charts/opentaco/values-test.yaml.example b/helm-charts/opentaco/values-test.yaml.example index 6ddbb1cbe..f07a73b58 100644 --- a/helm-charts/opentaco/values-test.yaml.example +++ b/helm-charts/opentaco/values-test.yaml.example @@ -26,7 +26,7 @@ cloudSql: # ============================================================================ # Components - All enabled with defaults # ============================================================================ -digger-managed: +taco-orchestrator: enabled: true # Note: imagePullSecrets not needed for public images # global: @@ -38,7 +38,7 @@ digger-managed: useExistingSecret: true existingSecretName: "backend-secrets" -statesman: +taco-statesman: enabled: true # Note: imagePullSecrets not needed for public images # global: @@ -54,7 +54,7 @@ statesman: storage: type: "s3" -drift: +taco-drift: enabled: true # Note: imagePullSecrets not needed for public images # global: @@ -64,7 +64,7 @@ drift: replicaCount: 1 existingSecretName: "drift-secrets" -ui: +taco-ui: enabled: true # Note: imagePullSecrets not needed for public images # imagePullSecrets: diff --git a/helm-charts/opentaco/values.yaml b/helm-charts/opentaco/values.yaml index cd06566c2..af539853e 100644 --- a/helm-charts/opentaco/values.yaml +++ b/helm-charts/opentaco/values.yaml @@ -4,6 +4,7 @@ # - Database (PostgreSQL or Cloud SQL) # - Digger Managed # - Taco Statesman +# - Token Service # - Drift Detection # - Taco UI @@ -59,9 +60,9 @@ cloudSql: serviceAccount: "cloudsql-sa" # ============================================================================ -# Digger Managed Configuration +# Taco Orchestrator Configuration # ============================================================================ -digger-managed: +taco-orchestrator: enabled: true digger: @@ -139,10 +140,84 @@ taco-statesman: # For detailed configuration (auth, JWT, postgres, OAuth, etc.), # see helm-charts/taco-statesman/values.yaml or use existingSecret above +# ============================================================================ +# Token Service Configuration +# ============================================================================ +taco-token-service: + enabled: true + + tokenService: + image: + repository: ghcr.io/diggerhq/digger/taco-token-service + tag: "v0.1.0" + pullPolicy: "IfNotPresent" + + replicaCount: 1 + + # Database configuration - Using Cloud SQL (same as other services) + database: + backend: postgres # Use postgres with Cloud SQL + + # SQLite (default - good for development) + sqlite: + path: "/app/data/tokens.db" + + # PostgreSQL (recommended for production) + postgres: + host: "127.0.0.1" # Cloud SQL proxy provides local access + port: 5432 + user: "postgres" # Match your Cloud SQL user + dbname: "taco" # Match your Cloud SQL database + sslmode: "disable" # Proxy handles SSL + secretName: "postgres-credentials" # Must contain 'password' key + + # MySQL + mysql: + host: "" + port: 3306 + user: "root" + dbname: "opentaco" + secretName: "" + + # MSSQL + mssql: + host: "" + port: 1433 + user: "" + dbname: "opentaco" + secretName: "" + + # Persistence (disabled - using Cloud SQL) + persistence: + enabled: false # Not needed with Cloud SQL + + # Cloud SQL configuration (enabled - same as statesman) + cloudSql: + enabled: true # Enable Cloud SQL proxy + instanceConnectionName: "YOUR-PROJECT-ID:YOUR-REGION:YOUR-INSTANCE" # Match your Cloud SQL instance + credentialsSecret: "cloudsql-credentials" # Match existing secret + serviceAccount: "cloudsql-sa" # Match existing SA + + # Resource limits (IMPORTANT: set for production) + resources: {} + # requests: + # memory: 256Mi + # cpu: 250m + # limits: + # memory: 1Gi + # cpu: 1000m + + # Service configuration + service: + type: ClusterIP + port: 8081 + + # For detailed configuration, see helm-charts/taco-token-service/values.yaml + # ============================================================================ # Drift Detection Configuration # ============================================================================ -drift: +taco-drift: enabled: true drift: @@ -201,8 +276,8 @@ taco-ui: # Backend service URLs (for server-side API calls) backends: - orchestratorUrl: "http://digger-managed:3000" - driftReportingUrl: "http://drift:3004" + orchestratorUrl: "http://taco-orchestrator:3000" + driftReportingUrl: "http://taco-drift:3004" statesmanUrl: "http://taco-statesman:8080" ingress: diff --git a/helm-charts/taco-drift/Chart.yaml b/helm-charts/taco-drift/Chart.yaml index c25a33dd3..cab35eeb0 100644 --- a/helm-charts/taco-drift/Chart.yaml +++ b/helm-charts/taco-drift/Chart.yaml @@ -1,6 +1,6 @@ apiVersion: v2 -name: drift -description: Digger Drift - Automated infrastructure drift detection and reporting service +name: taco-drift +description: Taco Drift - Automated infrastructure drift detection and reporting service type: application version: 0.1.0 appVersion: "v0.1.0" diff --git a/helm-charts/taco-orchestrator/Chart.yaml b/helm-charts/taco-orchestrator/Chart.yaml index 7079289a4..210c3e455 100644 --- a/helm-charts/taco-orchestrator/Chart.yaml +++ b/helm-charts/taco-orchestrator/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: digger-managed +name: taco-orchestrator description: A Helm chart for Digger Backend (Managed Version) - requires external secrets # A chart can be either an 'application' or a 'library' chart. diff --git a/helm-charts/taco-orchestrator/templates/_helpers.tpl b/helm-charts/taco-orchestrator/templates/_helpers.tpl index 502c34e45..03fe81939 100644 --- a/helm-charts/taco-orchestrator/templates/_helpers.tpl +++ b/helm-charts/taco-orchestrator/templates/_helpers.tpl @@ -1,7 +1,7 @@ {{/* Expand the name of the chart. */}} -{{- define "digger-managed.name" -}} +{{- define "taco-orchestrator.name" -}} {{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} {{- end }} @@ -10,7 +10,7 @@ Create a default fully qualified app name. We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). If release name contains chart name it will be used as a full name. */}} -{{- define "digger-managed.fullname" -}} +{{- define "taco-orchestrator.fullname" -}} {{- if .Values.fullnameOverride }} {{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} {{- else }} @@ -26,16 +26,16 @@ If release name contains chart name it will be used as a full name. {{/* Create chart name and version as used by the chart label. */}} -{{- define "digger-managed.chart" -}} +{{- define "taco-orchestrator.chart" -}} {{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} {{- end }} {{/* Common labels */}} -{{- define "digger-managed.labels" -}} -helm.sh/chart: {{ include "digger-managed.chart" . }} -{{ include "digger-managed.selectorLabels" . }} +{{- define "taco-orchestrator.labels" -}} +helm.sh/chart: {{ include "taco-orchestrator.chart" . }} +{{ include "taco-orchestrator.selectorLabels" . }} {{- if .Chart.AppVersion }} app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} {{- end }} @@ -45,7 +45,7 @@ app.kubernetes.io/managed-by: {{ .Release.Service }} {{/* Selector labels */}} -{{- define "digger-managed.selectorLabels" -}} -app.kubernetes.io/name: {{ include "digger-managed.name" . }} +{{- define "taco-orchestrator.selectorLabels" -}} +app.kubernetes.io/name: {{ include "taco-orchestrator.name" . }} app.kubernetes.io/instance: {{ .Release.Name }} {{- end }} diff --git a/helm-charts/taco-orchestrator/templates/backend-deployment.yaml b/helm-charts/taco-orchestrator/templates/backend-deployment.yaml index 1acbc9e02..d28954b41 100644 --- a/helm-charts/taco-orchestrator/templates/backend-deployment.yaml +++ b/helm-charts/taco-orchestrator/templates/backend-deployment.yaml @@ -1,19 +1,19 @@ apiVersion: apps/v1 kind: Deployment metadata: - name: {{ include "digger-managed.fullname" . }}-web + name: {{ include "taco-orchestrator.fullname" . }}-web labels: - {{- include "digger-managed.labels" . | nindent 4 }} + {{- include "taco-orchestrator.labels" . | nindent 4 }} spec: replicas: {{ .Values.digger.replicaCount }} selector: matchLabels: - app: {{ include "digger-managed.name" . }}-web + app: {{ include "taco-orchestrator.name" . }}-web template: metadata: labels: - app: {{ include "digger-managed.name" . }}-web - {{- include "digger-managed.selectorLabels" . | nindent 8 }} + app: {{ include "taco-orchestrator.name" . }}-web + {{- include "taco-orchestrator.selectorLabels" . | nindent 8 }} spec: {{- if .Values.global }} {{- with .Values.global.imagePullSecrets }} @@ -34,7 +34,7 @@ spec: {{- if .Values.digger.secret.useExistingSecret }} name: {{ .Values.digger.secret.existingSecretName }} {{- else }} - name: {{ include "digger-managed.fullname" . }}-secret + name: {{ include "taco-orchestrator.fullname" . }}-secret {{- end }} env: {{- if .Values.digger.postgres.existingSecretName }} diff --git a/helm-charts/taco-orchestrator/templates/backend-ingress.yaml b/helm-charts/taco-orchestrator/templates/backend-ingress.yaml index bc9192898..ec9fee1ec 100644 --- a/helm-charts/taco-orchestrator/templates/backend-ingress.yaml +++ b/helm-charts/taco-orchestrator/templates/backend-ingress.yaml @@ -13,13 +13,13 @@ apiVersion: extensions/v1beta1 {{- end }} kind: Ingress metadata: - name: {{ include "digger-managed.fullname" . }} + name: {{ include "taco-orchestrator.fullname" . }} annotations: {{- range $key, $value := .Values.digger.ingress.annotations }} {{ $key }}: {{ $value | quote }} {{- end }} labels: - {{- include "digger-managed.labels" . | nindent 4 }} + {{- include "taco-orchestrator.labels" . | nindent 4 }} spec: {{- if and .Values.digger.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} ingressClassName: {{ .Values.digger.ingress.className }} @@ -38,7 +38,7 @@ spec: pathType: Prefix backend: service: - name: {{ include "digger-managed.fullname" . }}-web + name: {{ include "taco-orchestrator.fullname" . }}-web port: number: {{ .Values.digger.service.port }} {{- end }} diff --git a/helm-charts/taco-orchestrator/templates/backend-service.yaml b/helm-charts/taco-orchestrator/templates/backend-service.yaml index 2859e7964..92392986d 100644 --- a/helm-charts/taco-orchestrator/templates/backend-service.yaml +++ b/helm-charts/taco-orchestrator/templates/backend-service.yaml @@ -1,11 +1,11 @@ apiVersion: v1 kind: Service metadata: - name: {{ include "digger-managed.fullname" . }}-web + name: {{ include "taco-orchestrator.fullname" . }}-web spec: type: {{ .Values.digger.service.type }} ports: - port: {{ .Values.digger.service.port }} targetPort: 3000 selector: - app: {{ include "digger-managed.name" . }}-web + app: {{ include "taco-orchestrator.name" . }}-web diff --git a/helm-charts/taco-orchestrator/templates/postgres-secret.yaml b/helm-charts/taco-orchestrator/templates/postgres-secret.yaml index 301396c55..c0ade77dc 100644 --- a/helm-charts/taco-orchestrator/templates/postgres-secret.yaml +++ b/helm-charts/taco-orchestrator/templates/postgres-secret.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "digger-managed.fullname" . }}-postgres-secret + name: {{ include "taco-orchestrator.fullname" . }}-postgres-secret labels: - {{- include "digger-managed.labels" . | nindent 4 }} + {{- include "taco-orchestrator.labels" . | nindent 4 }} type: Opaque stringData: postgres-password: {{ .Values.digger.postgres.password | quote }} diff --git a/helm-charts/taco-orchestrator/templates/secret.yaml b/helm-charts/taco-orchestrator/templates/secret.yaml index 7ca18e0b5..1ff8bcc19 100644 --- a/helm-charts/taco-orchestrator/templates/secret.yaml +++ b/helm-charts/taco-orchestrator/templates/secret.yaml @@ -2,9 +2,9 @@ apiVersion: v1 kind: Secret metadata: - name: {{ include "digger-managed.fullname" . }}-secret + name: {{ include "taco-orchestrator.fullname" . }}-secret labels: - {{- include "digger-managed.labels" . | nindent 4 }} + {{- include "taco-orchestrator.labels" . | nindent 4 }} type: Opaque stringData: # Database Configuration diff --git a/helm-charts/taco-statesman/Chart.yaml b/helm-charts/taco-statesman/Chart.yaml index fe1b1f26d..58b412b6a 100644 --- a/helm-charts/taco-statesman/Chart.yaml +++ b/helm-charts/taco-statesman/Chart.yaml @@ -1,5 +1,5 @@ apiVersion: v2 -name: statesman +name: taco-statesman description: Taco Statesman - Infrastructure-as-Code state management and coordination service type: application version: 0.1.0 diff --git a/helm-charts/taco-token-service/Chart.yaml b/helm-charts/taco-token-service/Chart.yaml new file mode 100644 index 000000000..d338c26f8 --- /dev/null +++ b/helm-charts/taco-token-service/Chart.yaml @@ -0,0 +1,8 @@ +apiVersion: v2 +name: taco-token-service +description: A Helm chart for Taco Token Service +type: application +version: 0.1.0 +appVersion: "v0.1.0" +icon: https://raw.githubusercontent.com/diggerhq/digger/main/docs/logo/digger-logo.png + diff --git a/helm-charts/taco-token-service/templates/_helpers.tpl b/helm-charts/taco-token-service/templates/_helpers.tpl new file mode 100644 index 000000000..69141f2b9 --- /dev/null +++ b/helm-charts/taco-token-service/templates/_helpers.tpl @@ -0,0 +1,59 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "taco-token-service.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "taco-token-service.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "taco-token-service.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "taco-token-service.labels" -}} +helm.sh/chart: {{ include "taco-token-service.chart" . }} +{{ include "taco-token-service.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "taco-token-service.selectorLabels" -}} +app.kubernetes.io/name: {{ include "taco-token-service.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "taco-token-service.serviceAccountName" -}} +{{- default "default" .Values.tokenService.serviceAccount.name }} +{{- end }} + diff --git a/helm-charts/taco-token-service/templates/deployment.yaml b/helm-charts/taco-token-service/templates/deployment.yaml new file mode 100644 index 000000000..eac043686 --- /dev/null +++ b/helm-charts/taco-token-service/templates/deployment.yaml @@ -0,0 +1,160 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ include "taco-token-service.fullname" . }} + labels: + {{- include "taco-token-service.labels" . | nindent 4 }} +spec: + replicas: {{ .Values.tokenService.replicaCount }} + selector: + matchLabels: + {{- include "taco-token-service.selectorLabels" . | nindent 6 }} + template: + metadata: + labels: + {{- include "taco-token-service.selectorLabels" . | nindent 8 }} + spec: + {{- if and .Values.tokenService.cloudSql .Values.tokenService.cloudSql.enabled }} + serviceAccountName: {{ .Values.tokenService.cloudSql.serviceAccount | default "default" }} + {{- end }} + containers: + - name: token-service + image: "{{ .Values.tokenService.image.repository }}:{{ .Values.tokenService.image.tag | default .Chart.AppVersion }}" + imagePullPolicy: {{ .Values.tokenService.image.pullPolicy | default "IfNotPresent" }} + ports: + - name: http + containerPort: {{ .Values.tokenService.service.port }} + protocol: TCP + env: + - name: OPENTACO_PORT + value: "{{ .Values.tokenService.service.port }}" + - name: OPENTACO_QUERY_BACKEND + value: "{{ .Values.tokenService.database.backend }}" + + # SQLite configuration + {{- if eq .Values.tokenService.database.backend "sqlite" }} + - name: OPENTACO_SQLITE_DB_PATH + value: "{{ .Values.tokenService.database.sqlite.path }}" + {{- end }} + + # PostgreSQL configuration + {{- if eq .Values.tokenService.database.backend "postgres" }} + - name: OPENTACO_POSTGRES_HOST + value: "{{ .Values.tokenService.database.postgres.host }}" + - name: OPENTACO_POSTGRES_PORT + value: "{{ .Values.tokenService.database.postgres.port }}" + - name: OPENTACO_POSTGRES_USER + value: "{{ .Values.tokenService.database.postgres.user }}" + - name: OPENTACO_POSTGRES_DBNAME + value: "{{ .Values.tokenService.database.postgres.dbname }}" + - name: OPENTACO_POSTGRES_SSLMODE + value: "{{ .Values.tokenService.database.postgres.sslmode }}" + {{- if .Values.tokenService.database.postgres.secretName }} + - name: OPENTACO_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.tokenService.database.postgres.secretName }} + key: password + {{- else if .Values.tokenService.database.postgres.password }} + - name: OPENTACO_POSTGRES_PASSWORD + value: "{{ .Values.tokenService.database.postgres.password }}" + {{- end }} + {{- end }} + + # MySQL configuration + {{- if eq .Values.tokenService.database.backend "mysql" }} + - name: OPENTACO_MYSQL_HOST + value: "{{ .Values.tokenService.database.mysql.host }}" + - name: OPENTACO_MYSQL_PORT + value: "{{ .Values.tokenService.database.mysql.port }}" + - name: OPENTACO_MYSQL_USER + value: "{{ .Values.tokenService.database.mysql.user }}" + - name: OPENTACO_MYSQL_DBNAME + value: "{{ .Values.tokenService.database.mysql.dbname }}" + {{- if .Values.tokenService.database.mysql.secretName }} + - name: OPENTACO_MYSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.tokenService.database.mysql.secretName }} + key: password + {{- else if .Values.tokenService.database.mysql.password }} + - name: OPENTACO_MYSQL_PASSWORD + value: "{{ .Values.tokenService.database.mysql.password }}" + {{- end }} + {{- end }} + + # MSSQL configuration + {{- if eq .Values.tokenService.database.backend "mssql" }} + - name: OPENTACO_MSSQL_HOST + value: "{{ .Values.tokenService.database.mssql.host }}" + - name: OPENTACO_MSSQL_PORT + value: "{{ .Values.tokenService.database.mssql.port }}" + - name: OPENTACO_MSSQL_USER + value: "{{ .Values.tokenService.database.mssql.user }}" + - name: OPENTACO_MSSQL_DBNAME + value: "{{ .Values.tokenService.database.mssql.dbname }}" + {{- if .Values.tokenService.database.mssql.secretName }} + - name: OPENTACO_MSSQL_PASSWORD + valueFrom: + secretKeyRef: + name: {{ .Values.tokenService.database.mssql.secretName }} + key: password + {{- else if .Values.tokenService.database.mssql.password }} + - name: OPENTACO_MSSQL_PASSWORD + value: "{{ .Values.tokenService.database.mssql.password }}" + {{- end }} + {{- end }} + + {{- if and (eq .Values.tokenService.database.backend "sqlite") .Values.tokenService.persistence.enabled }} + volumeMounts: + - name: data + mountPath: /app/data + {{- end }} + + livenessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 30 + periodSeconds: 10 + readinessProbe: + httpGet: + path: /healthz + port: http + initialDelaySeconds: 5 + periodSeconds: 5 + + {{- if and .Values.tokenService.cloudSql .Values.tokenService.cloudSql.enabled }} + - name: cloud-sql-proxy + image: gcr.io/cloud-sql-connectors/cloud-sql-proxy:2.11.0 + args: + - "--structured-logs" + - "--port=5432" + - "{{ .Values.tokenService.cloudSql.instanceConnectionName }}" + securityContext: + runAsNonRoot: true + {{- if .Values.tokenService.cloudSql.credentialsSecret }} + env: + - name: GOOGLE_APPLICATION_CREDENTIALS + value: /secrets/cloudsql/credentials.json + volumeMounts: + - name: cloudsql-credentials + mountPath: /secrets/cloudsql + readOnly: true + {{- end }} + {{- end }} + + {{- if or (and (eq .Values.tokenService.database.backend "sqlite") .Values.tokenService.persistence.enabled) (and .Values.tokenService.cloudSql .Values.tokenService.cloudSql.enabled) }} + volumes: + {{- if and (eq .Values.tokenService.database.backend "sqlite") .Values.tokenService.persistence.enabled }} + - name: data + persistentVolumeClaim: + claimName: {{ include "taco-token-service.fullname" . }}-data + {{- end }} + {{- if and .Values.tokenService.cloudSql .Values.tokenService.cloudSql.enabled .Values.tokenService.cloudSql.credentialsSecret }} + - name: cloudsql-credentials + secret: + secretName: {{ .Values.tokenService.cloudSql.credentialsSecret }} + {{- end }} + {{- end }} + diff --git a/helm-charts/taco-token-service/templates/pvc.yaml b/helm-charts/taco-token-service/templates/pvc.yaml new file mode 100644 index 000000000..8c71a0180 --- /dev/null +++ b/helm-charts/taco-token-service/templates/pvc.yaml @@ -0,0 +1,18 @@ +{{- if and (eq .Values.tokenService.database.backend "sqlite") .Values.tokenService.persistence.enabled }} +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: {{ include "taco-token-service.fullname" . }}-data + labels: + {{- include "taco-token-service.labels" . | nindent 4 }} +spec: + accessModes: + - {{ .Values.tokenService.persistence.accessMode }} + resources: + requests: + storage: {{ .Values.tokenService.persistence.size }} + {{- if .Values.tokenService.persistence.storageClass }} + storageClassName: {{ .Values.tokenService.persistence.storageClass }} + {{- end }} +{{- end }} + diff --git a/helm-charts/taco-token-service/templates/service.yaml b/helm-charts/taco-token-service/templates/service.yaml new file mode 100644 index 000000000..79d7ab786 --- /dev/null +++ b/helm-charts/taco-token-service/templates/service.yaml @@ -0,0 +1,16 @@ +apiVersion: v1 +kind: Service +metadata: + name: {{ include "taco-token-service.fullname" . }} + labels: + {{- include "taco-token-service.labels" . | nindent 4 }} +spec: + type: {{ .Values.tokenService.service.type }} + ports: + - port: {{ .Values.tokenService.service.port }} + targetPort: {{ .Values.tokenService.service.port }} + protocol: TCP + name: http + selector: + {{- include "taco-token-service.selectorLabels" . | nindent 4 }} + diff --git a/helm-charts/taco-token-service/values.yaml b/helm-charts/taco-token-service/values.yaml new file mode 100644 index 000000000..f01f818ed --- /dev/null +++ b/helm-charts/taco-token-service/values.yaml @@ -0,0 +1,64 @@ +# values.yaml + +tokenService: + # Image configuration + image: + repository: ghcr.io/diggerhq/digger/taco-token-service + tag: "v0.1.0" + pullPolicy: "IfNotPresent" + + # Number of replicas + replicaCount: 1 + + # Service configuration + service: + type: ClusterIP + port: 8081 + + # Database configuration + database: + # Backend type: "sqlite", "postgres", "mysql", "mssql" + backend: "sqlite" + + # SQLite configuration (default) + sqlite: + path: "/app/data/tokens.db" + + # PostgreSQL configuration + postgres: + host: "" + port: 5432 + user: "postgres" + password: "" + dbname: "taco" + sslmode: "disable" + # Use Kubernetes secrets for credentials + secretName: "" + + # MySQL configuration + mysql: + host: "" + port: 3306 + user: "root" + password: "" + dbname: "taco" + # Use Kubernetes secrets for credentials + secretName: "" + + # MSSQL configuration + mssql: + host: "" + port: 1433 + user: "" + password: "" + dbname: "taco" + # Use Kubernetes secrets for credentials + secretName: "" + + # Persistence for SQLite + persistence: + enabled: true + size: 1Gi + storageClass: "" + accessMode: ReadWriteOnce + diff --git a/helm-charts/taco-ui/values.yaml b/helm-charts/taco-ui/values.yaml index ec315660b..795343046 100644 --- a/helm-charts/taco-ui/values.yaml +++ b/helm-charts/taco-ui/values.yaml @@ -59,7 +59,7 @@ ui: # STATESMAN_BACKEND_URL, STATESMAN_BACKEND_WEBHOOK_SECRET backends: # Orchestrator (Digger Backend) - orchestratorUrl: "" # ORCHESTRATOR_BACKEND_URL (e.g., http://opentaco-digger-managed-web:3000) -> the orchestrator is the taco-orchestrator service + orchestratorUrl: "" # ORCHESTRATOR_BACKEND_URL (e.g., http://opentaco-taco-orchestrator-web:3000) orchestratorSecret: "" # ORCHESTRATOR_BACKEND_SECRET # Drift Reporting diff --git a/taco/Dockerfile_token_service b/taco/Dockerfile_token_service new file mode 100644 index 000000000..111a74c0b --- /dev/null +++ b/taco/Dockerfile_token_service @@ -0,0 +1,56 @@ +FROM golang:1.24 AS builder +ARG COMMIT_SHA +RUN echo "commit sha: ${COMMIT_SHA}" + +# Set the working directory +WORKDIR /go/src/github.com/diggerhq/digger/taco + +# Copy all source code +COPY cmd/token_service/ ./cmd/token_service/ +COPY internal/ ./internal/ + +# Download dependencies and build +RUN cd cmd/token_service && \ + go mod tidy && \ + CGO_ENABLED=0 GOOS=linux go build \ + -ldflags="-X 'main.Version=${COMMIT_SHA}' -s -w" \ + -a -installsuffix cgo \ + -o token_service . + +# Multi-stage build - use a minimal image for runtime +FROM ubuntu:24.04 AS runner +ARG COMMIT_SHA +WORKDIR /app + +# Install ca-certificates for HTTPS requests +RUN apt-get update && \ + apt-get install -y ca-certificates curl && \ + apt-get clean && \ + rm -rf /var/lib/apt/lists/* + +RUN echo "commit sha: ${COMMIT_SHA}" + +# Copy the binary from builder stage +COPY --from=builder /go/src/github.com/diggerhq/digger/taco/cmd/token_service/token_service /app/token_service + +# Make the binary executable +RUN chmod +x /app/token_service + +# Expose the port that token service runs on +EXPOSE 8081 + +# Set environment variables with defaults +ENV OPENTACO_PORT=8081 +ENV OPENTACO_QUERY_BACKEND=sqlite +ENV OPENTACO_SQLITE_DB_PATH=/app/data/tokens.db + +# Create data directory +RUN mkdir -p /app/data + +# Health check +HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \ + CMD curl -f http://localhost:8081/healthz || exit 1 + +# Run the token service binary +ENTRYPOINT ["/app/token_service"] + diff --git a/taco/Makefile b/taco/Makefile index 86197fafe..8660e9119 100644 --- a/taco/Makefile +++ b/taco/Makefile @@ -6,7 +6,7 @@ # Build all components all: build ## Build all components -build: build-svc build-cli build-prov ## Build service, CLI, and provider +build: build-svc build-cli build-prov build-token ## Build service, CLI, provider, and token service build-svc: ## Build the OpenTaco service @echo "Building OpenTaco service..." @@ -20,7 +20,11 @@ build-prov: ## Build the Terraform provider @echo "Building Terraform provider..." cd providers/terraform/opentaco && GOWORK=off go build -o ../../../terraform-provider-opentaco . -install: install-svc install-cli ## Install service and CLI +build-token: ## Build the Token Service + @echo "Building Token Service..." + cd cmd/token_service && GOWORK=off go build -o ../../token_service . + +install: install-svc install-cli install-token ## Install service, CLI, and token service install-svc: ## Build the OpenTaco service @echo "Installing OpenTaco service..." @@ -30,6 +34,10 @@ install-cli: ## Build the taco CLI @echo "Installing taco CLI..." cd cmd/taco && GOWORK=off go install . +install-token: ## Install the Token Service + @echo "Installing Token Service..." + cd cmd/token_service && GOWORK=off go install . + # Run components svc: build-svc ## Run the OpenTaco service @echo "Starting OpenTaco service on :8080..." @@ -39,6 +47,10 @@ cli: build-cli ## Build and show CLI help @echo "Taco CLI built. Run ./taco --help for usage" ./taco --help +token: build-token ## Run the Token Service + @echo "Starting Token Service on :8081..." + ./token_service + # Development test: ## Run tests @echo "Running tests..." @@ -53,7 +65,7 @@ lint: ## Run linters clean: ## Clean build artifacts @echo "Cleaning build artifacts..." - rm -f statesman taco terraform-provider-opentaco + rm -f statesman taco terraform-provider-opentaco token_service rm -rf .devdata/* find . -name "*.test" -delete find . -name "*.out" -delete @@ -79,11 +91,26 @@ docker-run-svc: ## Run statesman in Docker container --name statesman-container \ statesman:latest +docker-build-token: ## Build token service Docker image + @echo "Building token service Docker image..." + docker build -f Dockerfile_token_service -t token-service:latest --build-arg COMMIT_SHA=$(shell git rev-parse HEAD) . + +docker-run-token: ## Run token service in Docker container + @echo "Running token service in Docker container..." + docker run -p 8081:8081 \ + -e OPENTACO_QUERY_BACKEND=sqlite \ + -e OPENTACO_SQLITE_DB_PATH=/app/data/tokens.db \ + --name token-service-container \ + token-service:latest + docker-clean: ## Clean Docker images and containers @echo "Cleaning Docker artifacts..." docker stop statesman-container 2>/dev/null || true docker rm statesman-container 2>/dev/null || true docker rmi statesman:latest 2>/dev/null || true + docker stop token-service-container 2>/dev/null || true + docker rm token-service-container 2>/dev/null || true + docker rmi token-service:latest 2>/dev/null || true help: ## Show this help @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-15s\033[0m %s\n", $$1, $$2}' diff --git a/taco/cmd/token_service/examples.sh b/taco/cmd/token_service/examples.sh new file mode 100755 index 000000000..680d467d2 --- /dev/null +++ b/taco/cmd/token_service/examples.sh @@ -0,0 +1,107 @@ +#!/bin/bash + +# Token Service API Examples +# Make sure the token service is running on localhost:8081 + +set -e + +BASE_URL="${TOKEN_SERVICE_URL:-http://localhost:8081}" +USER_ID="user-123" +ORG_ID="org-456" + +echo "============================================" +echo "Token Service API Examples" +echo "============================================" +echo "" + +# Health Check +echo "1. Health Check" +echo " GET $BASE_URL/healthz" +curl -s "$BASE_URL/healthz" | jq '.' +echo "" +echo "" + +# Create a Token +echo "2. Create Token" +echo " POST $BASE_URL/api/v1/tokens" +TOKEN_RESPONSE=$(curl -s -X POST "$BASE_URL/api/v1/tokens" \ + -H "Content-Type: application/json" \ + -d "{ + \"user_id\": \"$USER_ID\", + \"org_id\": \"$ORG_ID\", + \"name\": \"Example API Token\", + \"expires_in\": \"720h\" + }") + +echo "$TOKEN_RESPONSE" | jq '.' +TOKEN_ID=$(echo "$TOKEN_RESPONSE" | jq -r '.id') +TOKEN_VALUE=$(echo "$TOKEN_RESPONSE" | jq -r '.token') +echo "" +echo " Created token ID: $TOKEN_ID" +echo " Token value: $TOKEN_VALUE" +echo "" +echo "" + +# List All Tokens +echo "3. List All Tokens" +echo " GET $BASE_URL/api/v1/tokens" +curl -s "$BASE_URL/api/v1/tokens" | jq '.' +echo "" +echo "" + +# List Tokens by User +echo "4. List Tokens by User ID" +echo " GET $BASE_URL/api/v1/tokens?user_id=$USER_ID" +curl -s "$BASE_URL/api/v1/tokens?user_id=$USER_ID" | jq '.' +echo "" +echo "" + +# List Tokens by Org +echo "5. List Tokens by Org ID" +echo " GET $BASE_URL/api/v1/tokens?org_id=$ORG_ID" +curl -s "$BASE_URL/api/v1/tokens?org_id=$ORG_ID" | jq '.' +echo "" +echo "" + +# Get Specific Token +echo "6. Get Token by ID" +echo " GET $BASE_URL/api/v1/tokens/$TOKEN_ID" +curl -s "$BASE_URL/api/v1/tokens/$TOKEN_ID" | jq '.' +echo "" +echo "" + +# Verify Token +echo "7. Verify Token" +echo " POST $BASE_URL/api/v1/tokens/verify" +curl -s -X POST "$BASE_URL/api/v1/tokens/verify" \ + -H "Content-Type: application/json" \ + -d "{ + \"token\": \"$TOKEN_VALUE\", + \"user_id\": \"$USER_ID\", + \"org_id\": \"$ORG_ID\" + }" | jq '.' +echo "" +echo "" + +# Delete Token +echo "8. Delete Token" +echo " DELETE $BASE_URL/api/v1/tokens/$TOKEN_ID" +curl -s -X DELETE "$BASE_URL/api/v1/tokens/$TOKEN_ID" | jq '.' +echo "" +echo "" + +# Verify Deleted Token (should fail) +echo "9. Verify Deleted Token (should fail)" +echo " POST $BASE_URL/api/v1/tokens/verify" +curl -s -X POST "$BASE_URL/api/v1/tokens/verify" \ + -H "Content-Type: application/json" \ + -d "{ + \"token\": \"$TOKEN_VALUE\" + }" | jq '.' +echo "" +echo "" + +echo "============================================" +echo "Examples Complete!" +echo "============================================" + diff --git a/taco/cmd/token_service/go.mod b/taco/cmd/token_service/go.mod new file mode 100644 index 000000000..350ea17d3 --- /dev/null +++ b/taco/cmd/token_service/go.mod @@ -0,0 +1,66 @@ +module github.com/diggerhq/digger/opentaco/cmd/token_service + +go 1.24.0 + +replace github.com/diggerhq/digger/opentaco/internal => ../../internal + +require ( + github.com/diggerhq/digger/opentaco/internal v0.0.0-00010101000000-000000000000 + github.com/kelseyhightower/envconfig v1.4.0 + github.com/labstack/echo/v4 v4.13.3 +) + +require ( + filippo.io/edwards25519 v1.1.0 // indirect + github.com/aws/aws-sdk-go-v2 v1.38.1 // indirect + github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 // indirect + github.com/aws/aws-sdk-go-v2/config v1.31.2 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.6 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect + github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.4 // indirect + github.com/aws/aws-sdk-go-v2/service/s3 v1.87.1 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 // indirect + github.com/aws/smithy-go v1.22.5 // indirect + github.com/coreos/go-oidc/v3 v3.11.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.2 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect + github.com/golang-sql/sqlexp v0.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/jackc/pgpassfile v1.0.0 // indirect + github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect + github.com/jackc/pgx/v5 v5.6.0 // indirect + github.com/jackc/puddle/v2 v2.2.2 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/labstack/gommon v0.4.2 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.28 // indirect + github.com/microsoft/go-mssqldb v1.8.2 // indirect + github.com/mr-tron/base58 v1.2.0 // indirect + github.com/valyala/bytebufferpool v1.0.0 // indirect + github.com/valyala/fasttemplate v1.2.2 // indirect + golang.org/x/crypto v0.41.0 // indirect + golang.org/x/net v0.43.0 // indirect + golang.org/x/oauth2 v0.30.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/time v0.8.0 // indirect + gorm.io/driver/mysql v1.6.0 // indirect + gorm.io/driver/postgres v1.6.0 // indirect + gorm.io/driver/sqlite v1.6.0 // indirect + gorm.io/driver/sqlserver v1.6.1 // indirect + gorm.io/gorm v1.31.0 // indirect +) diff --git a/taco/cmd/token_service/go.sum b/taco/cmd/token_service/go.sum new file mode 100644 index 000000000..22e66d105 --- /dev/null +++ b/taco/cmd/token_service/go.sum @@ -0,0 +1,292 @@ +filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= +filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.1/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0 h1:g0EZJwz7xkXQiZAI5xi9f3WWFYBlX1CPTrR+NDToRkQ= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.17.0/go.mod h1:XCW7KnZet0Opnr7HccfUw1PLc4CjHqpcaxW8DHklNkQ= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.1/go.mod h1:uE9zaUfEQT/nbQjVi2IblCG9iaLtZsuYZ8ne+PuQ02M= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0 h1:B/dfvscEQtew9dVuoxqxrUKKv8Ih2f55PydknDamU+g= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.8.0/go.mod h1:fiPSssYvltE08HJchL04dOy+RD4hgrjph0cwGGMntdI= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.5.2/go.mod h1:yInRyqWXAuaPrgI7p70+lDDgh3mlBohis29jGMISnmc= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1 h1:MyVTgWR8qd/Jw1Le0NZebGBUCLbtak3bJ3z1OlqZBpw= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.0.1/go.mod h1:GpPjLhVR9dnUoJMyHWSPy71xY9/lcmpzIPZXmF0FCVY= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0 h1:D3occbWoio4EBLkbkevetNMAVX197GkzbUMtqjGWn80= +github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.0.0/go.mod h1:bTSOgj05NGRuHHhQwAdPnYr9TOdNmKlZTgGLL6nyAdI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.1.1/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2 h1:kYRSnvJju5gYVyhkij+RTJ/VR6QIUaCfWeaFm2ycsjQ= +github.com/AzureAD/microsoft-authentication-library-for-go v1.3.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= +github.com/aws/aws-sdk-go-v2 v1.38.1 h1:j7sc33amE74Rz0M/PoCpsZQ6OunLqys/m5antM0J+Z8= +github.com/aws/aws-sdk-go-v2 v1.38.1/go.mod h1:9Q0OoGQoboYIAJyslFyF1f5K1Ryddop8gqMhWx/n4Wg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0 h1:6GMWV6CNpA/6fbFHnoAjrv4+LGfyTqZz2LtCHnspgDg= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0/go.mod h1:/mXlTIVG9jbxkqDnr5UQNQxW1HRYxeGklkM9vAFeabg= +github.com/aws/aws-sdk-go-v2/config v1.31.2 h1:NOaSZpVGEH2Np/c1toSeW0jooNl+9ALmsUTZ8YvkJR0= +github.com/aws/aws-sdk-go-v2/config v1.31.2/go.mod h1:17ft42Yb2lF6OigqSYiDAiUcX4RIkEMY6XxEMJsrAes= +github.com/aws/aws-sdk-go-v2/credentials v1.18.6 h1:AmmvNEYrru7sYNJnp3pf57lGbiarX4T9qU/6AZ9SucU= +github.com/aws/aws-sdk-go-v2/credentials v1.18.6/go.mod h1:/jdQkh1iVPa01xndfECInp1v1Wnp70v3K4MvtlLGVEc= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4 h1:lpdMwTzmuDLkgW7086jE94HweHCqG+uOJwHf3LZs7T0= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.4/go.mod h1:9xzb8/SV62W6gHQGC/8rrvgNXU6ZoYM3sAIJCIrXJxY= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4 h1:IdCLsiiIj5YJ3AFevsewURCPV+YWUlOW8JiPhoAy8vg= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.4/go.mod h1:l4bdfCD7XyyZA9BolKBo1eLqgaJxl0/x91PL4Yqe0ao= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4 h1:j7vjtr1YIssWQOMeOWRbh3z8g2oY/xPjnZH2gLY4sGw= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.4/go.mod h1:yDmJgqOiH4EA8Hndnv4KwAo8jCGTSnM5ASG1nBI+toA= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.4 h1:BE/MNQ86yzTINrfxPPFS86QCBNQeLKY2A0KhDh47+wI= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.4/go.mod h1:SPBBhkJxjcrzJBc+qY85e83MQ2q3qdra8fghhkkyrJg= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0 h1:6+lZi2JeGKtCraAj1rpoZfKqnQ9SptseRZioejfUOLM= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.0/go.mod h1:eb3gfbVIxIoGgJsi9pGne19dhCBpK6opTYpQqAmdy44= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.4 h1:Beh9oVgtQnBgR4sKKzkUBRQpf1GnL4wt0l4s8h2VCJ0= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.4/go.mod h1:b17At0o8inygF+c6FOD3rNyYZufPw62o9XJbSfQPgbo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4 h1:ueB2Te0NacDMnaC+68za9jLwkjzxGWm0KB5HTUHjLTI= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.4/go.mod h1:nLEfLnVMmLvyIG58/6gsSA03F1voKGaCfHV7+lR8S7s= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.4 h1:HVSeukL40rHclNcUqVcBwE1YoZhOkoLeBfhUqR3tjIU= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.4/go.mod h1:DnbBOv4FlIXHj2/xmrUQYtawRFC9L9ZmQPz+DBc6X5I= +github.com/aws/aws-sdk-go-v2/service/s3 v1.87.1 h1:2n6Pd67eJwAb/5KCX62/8RTU0aFAAW7V5XIGSghiHrw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.87.1/go.mod h1:w5PC+6GHLkvMJKasYGVloB3TduOtROEMqm15HSuIbw4= +github.com/aws/aws-sdk-go-v2/service/sso v1.28.2 h1:ve9dYBB8CfJGTFqcQ3ZLAAb/KXWgYlgu/2R2TZL2Ko0= +github.com/aws/aws-sdk-go-v2/service/sso v1.28.2/go.mod h1:n9bTZFZcBa9hGGqVz3i/a6+NG0zmZgtkB9qVVFDqPA8= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2 h1:pd9G9HQaM6UZAZh19pYOkpKSQkyQQ9ftnl/LttQOcGI= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.33.2/go.mod h1:eknndR9rU8UpE/OmFpqU78V1EcXPKFTTm5l/buZYgvM= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.0 h1:iV1Ko4Em/lkJIsoKyGfc0nQySi+v0Udxr6Igq+y9JZc= +github.com/aws/aws-sdk-go-v2/service/sts v1.38.0/go.mod h1:bEPcjW7IbolPfK67G1nilqWyoxYMSPrDiIQ3RdIdKgo= +github.com/aws/smithy-go v1.22.5 h1:P9ATCXPMb2mPjYBgueqJNCA5S9UfktsW0tTxi+a7eqw= +github.com/aws/smithy-go v1.22.5/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= +github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/OtI= +github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dnaeon/go-vcr v1.1.0/go.mod h1:M7tiix8f0r6mKKJ3Yq/kqU1OYf3MnfmBWVbPx/yU9ko= +github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= +github.com/go-jose/go-jose/v4 v4.1.2 h1:TK/7NqRQZfgAh+Td8AlsrvtPoUyiHh0LqVvokh+1vHI= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= +github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= +github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg= +github.com/golang-jwt/jwt/v5 v5.0.0/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA= +github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= +github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A= +github.com/golang-sql/sqlexp v0.1.0/go.mod h1:J4ad9Vo8ZCWQ2GMrC4UCQy1JpCbwU9m3EOqtpKwwwHI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo= +github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY= +github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw= +github.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo= +github.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= +github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= +github.com/jcmturner/dnsutils/v2 v2.0.0/go.mod h1:b0TnjGOvI/n42bZa+hmXL+kFJZsFT7G4t3HTlQ184QM= +github.com/jcmturner/gofork v1.7.6/go.mod h1:1622LH6i/EZqLloHfE7IeZ0uEJwMSUyQ/nDd82IeqRo= +github.com/jcmturner/goidentity/v6 v6.0.1/go.mod h1:X1YW3bgtvwAXju7V3LCIMpY0Gbxyjn/mY9zx4tFonSg= +github.com/jcmturner/gokrb5/v8 v8.4.4/go.mod h1:1btQEpgT6k+unzCwX1KdWMEwPPkkgBtP+F6aCACiMrs= +github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/kelseyhightower/envconfig v1.4.0 h1:Im6hONhd3pLkfDFsbRgu68RDNkGF1r3dvMUtDTo2cv8= +github.com/kelseyhightower/envconfig v1.4.0/go.mod h1:cccZRl6mQpaq41TPp5QxidR+Sa3axMbJDNb//FQX6Gg= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/labstack/echo/v4 v4.13.3 h1:pwhpCPrTl5qry5HRdM5FwdXnhXSLSY+WE+YQSeCaafY= +github.com/labstack/echo/v4 v4.13.3/go.mod h1:o90YNEeQWjDozo584l7AwhJMHN0bOC4tAfg+Xox9q5g= +github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= +github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEum7A= +github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/microsoft/go-mssqldb v1.8.2 h1:236sewazvC8FvG6Dr3bszrVhMkAl4KYImryLkRMCd0I= +github.com/microsoft/go-mssqldb v1.8.2/go.mod h1:vp38dT33FGfVotRiTmDo3bFyaHq+p3LektQrjTULowo= +github.com/modocache/gover v0.0.0-20171022184752-b58185e213c5/go.mod h1:caMODM3PzxT8aQXRPkAt8xlV/e7d7w8GM5g0fa5F0D8= +github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow= +github.com/mr-tron/base58 v1.2.0 h1:T/HDJBh4ZCPbU39/+c3rRvE0uKBQlU27+QI8LJ4t64o= +github.com/mr-tron/base58 v1.2.0/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjWI2mNwc= +github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= +github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= +github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= +github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= +golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= +golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/crypto v0.41.0 h1:WKYxWedPGCTVVl5+WHSSrOBT0O8lx32+zxmHxijgXp4= +golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= +golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= +golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE= +golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg= +golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= +golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= +golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= +golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= +golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg= +golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= +gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= +gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= +gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/driver/sqlserver v1.6.1 h1:XWISFsu2I2pqd1KJhhTZNJMx1jNQ+zVL/Q8ovDcUjtY= +gorm.io/driver/sqlserver v1.6.1/go.mod h1:VZeNn7hqX1aXoN5TPAFGWvxWG90xtA8erGn2gQmpc6U= +gorm.io/gorm v1.30.0/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= +gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY= +gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/taco/cmd/token_service/main.go b/taco/cmd/token_service/main.go new file mode 100644 index 000000000..9fa6b7075 --- /dev/null +++ b/taco/cmd/token_service/main.go @@ -0,0 +1,97 @@ +package main + +import ( + "context" + "flag" + "fmt" + "log" + "net/http" + "os" + "os/signal" + "time" + + "github.com/diggerhq/digger/opentaco/internal/query" + "github.com/diggerhq/digger/opentaco/internal/queryfactory" + "github.com/diggerhq/digger/opentaco/internal/repositories" + "github.com/diggerhq/digger/opentaco/internal/token_service" + "github.com/kelseyhightower/envconfig" + "github.com/labstack/echo/v4" + echomiddleware "github.com/labstack/echo/v4/middleware" +) + +// change this random number to bump version of token service: 1 +func main() { + var ( + port = flag.String("port", "8081", "Server port") + ) + flag.Parse() + + // Load configuration from environment variables into our struct. + var queryCfg query.Config + err := envconfig.Process("opentaco", &queryCfg) // The prefix "OPENTACO" will be used for all vars. + if err != nil { + log.Fatalf("Failed to process configuration: %v", err) + } + + // --- Initialize Stores --- + + // Create the database query store using the dedicated factory. + queryStore, err := queryfactory.NewQueryStore(queryCfg) + if err != nil { + log.Fatalf("Failed to initialize query backend: %v", err) + } + defer queryStore.Close() + + log.Printf("Query backend initialized: %s", queryCfg.Backend) + + // Get the underlying *gorm.DB from the query store + db := repositories.GetDBFromQueryStore(queryStore) + if db == nil { + log.Fatalf("Query store does not provide GetDB method") + } + + // Create token repository + tokenRepo := token_service.NewTokenRepository(db) + log.Println("Token repository initialized") + + // Create Echo instance + e := echo.New() + e.HideBanner = true + + // Middleware + e.Use(echomiddleware.Logger()) + e.Use(echomiddleware.Recover()) + e.Use(echomiddleware.RequestID()) + e.Use(echomiddleware.Gzip()) + e.Use(echomiddleware.Secure()) + e.Use(echomiddleware.CORS()) + + // Register routes + token_service.RegisterRoutes(e, tokenRepo) + + // Start server + go func() { + addr := fmt.Sprintf(":%s", *port) + log.Printf("Starting Token Service on %s", addr) + if err := e.Start(addr); err != nil && err != http.ErrServerClosed { + log.Fatalf("Server startup failed: %v", err) + } + }() + + // Wait for interrupt signal to gracefully shutdown the server + quit := make(chan os.Signal, 1) + signal.Notify(quit, os.Interrupt) + <-quit + + // Graceful shutdown + log.Println("Shutting down server...") + shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second) + defer cancel() + + if err := e.Shutdown(shutdownCtx); err != nil { + log.Fatalf("Server shutdown failed: %v", err) + } + + log.Println("Server shutdown complete") +} + diff --git a/taco/internal/atlas_loader.go b/taco/internal/atlas_loader.go index fc2833433..55cfd9163 100644 --- a/taco/internal/atlas_loader.go +++ b/taco/internal/atlas_loader.go @@ -110,6 +110,7 @@ func adaptSQL(sql, dialect string) string { sql = strings.ReplaceAll(sql, "integer", "int") // Fix DEFAULT clause - PostgreSQL uses single quotes for string literals sql = strings.ReplaceAll(sql, "DEFAULT \"allow\"", "DEFAULT 'allow'") + sql = strings.ReplaceAll(sql, "DEFAULT \"active\"", "DEFAULT 'active'") sql = strings.ReplaceAll(sql, "DEFAULT \"\"", "DEFAULT ''") case "mysql": // MySQL uses backticks (already correct from SQLite) diff --git a/taco/internal/query/types/models.go b/taco/internal/query/types/models.go index 038318ff7..d3f1f5165 100644 --- a/taco/internal/query/types/models.go +++ b/taco/internal/query/types/models.go @@ -195,6 +195,28 @@ type RolePermission struct { } func (RolePermission) TableName() string { return "role_permissions" } +type Token struct { + ID string `gorm:"type:varchar(36);primaryKey"` + UserID string `gorm:"type:varchar(255);index;not null"` // Flexible for external user IDs + OrgID string `gorm:"type:varchar(255);index;not null"` // Flexible for external org IDs + Token string `gorm:"type:varchar(255);uniqueIndex;not null"` + Name string `gorm:"type:varchar(255)"` + Status string `gorm:"type:varchar(20);default:active"` + CreatedAt time.Time + UpdatedAt time.Time + LastUsedAt *time.Time + ExpiresAt *time.Time +} + +func (t *Token) BeforeCreate(tx *gorm.DB) error { + if t.ID == "" { + t.ID = uuid.New().String() + } + return nil +} + +func (Token) TableName() string { return "tokens" } + var DefaultModels = []any{ &Organization{}, &User{}, @@ -209,4 +231,5 @@ var DefaultModels = []any{ &Unit{}, &Tag{}, &UnitTag{}, + &Token{}, } \ No newline at end of file diff --git a/taco/internal/token_service/handler.go b/taco/internal/token_service/handler.go new file mode 100644 index 000000000..fd5fab6c4 --- /dev/null +++ b/taco/internal/token_service/handler.go @@ -0,0 +1,190 @@ +package token_service + +import ( + "log" + "net/http" + "time" + + "github.com/diggerhq/digger/opentaco/internal/query/types" + "github.com/labstack/echo/v4" +) + +// Handler handles HTTP requests for token operations +type Handler struct { + repo *TokenRepository +} + +// NewHandler creates a new token handler +func NewHandler(repo *TokenRepository) *Handler { + return &Handler{repo: repo} +} + +// CreateTokenRequest represents the request to create a new token +type CreateTokenRequest struct { + UserID string `json:"user_id" validate:"required"` + OrgID string `json:"org_id" validate:"required"` + Name string `json:"name"` + ExpiresIn *string `json:"expires_in"` // Duration string like "24h", "7d", etc. +} + +// TokenResponse represents the response for a token +type TokenResponse struct { + ID string `json:"id"` + UserID string `json:"user_id"` + OrgID string `json:"org_id"` + Token string `json:"token"` + Name string `json:"name"` + Status string `json:"status"` + CreatedAt time.Time `json:"created_at"` + UpdatedAt time.Time `json:"updated_at"` + LastUsedAt *time.Time `json:"last_used_at,omitempty"` + ExpiresAt *time.Time `json:"expires_at,omitempty"` +} + +// VerifyTokenRequest represents the request to verify a token +type VerifyTokenRequest struct { + Token string `json:"token" validate:"required"` + UserID string `json:"user_id"` + OrgID string `json:"org_id"` +} + +// CreateToken creates a new token +func (h *Handler) CreateToken(c echo.Context) error { + var req CreateTokenRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request"}) + } + + if req.UserID == "" || req.OrgID == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "user_id and org_id are required"}) + } + + // Parse expiration duration if provided + var expiresAt *time.Time + if req.ExpiresIn != nil && *req.ExpiresIn != "" { + duration, err := time.ParseDuration(*req.ExpiresIn) + if err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid expires_in format. Use duration like '24h' (hours), '30m' (minutes), '168h' (7 days)."}) + } + exp := time.Now().UTC().Add(duration) // Always use UTC + expiresAt = &exp + } + + token, err := h.repo.CreateToken(c.Request().Context(), req.UserID, req.OrgID, req.Name, expiresAt) + if err != nil { + log.Printf("Failed to create token: %v", err) + return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to create token"}) + } + + return c.JSON(http.StatusCreated, toTokenResponse(token)) +} + +// ListTokens lists all tokens for a user and org +func (h *Handler) ListTokens(c echo.Context) error { + userID := c.QueryParam("user_id") + orgID := c.QueryParam("org_id") + + tokens, err := h.repo.ListTokens(c.Request().Context(), userID, orgID) + if err != nil { + log.Printf("Failed to list tokens: %v", err) + return c.JSON(http.StatusInternalServerError, map[string]string{"error": "Failed to list tokens"}) + } + + responses := make([]TokenResponse, len(tokens)) + for i, token := range tokens { + responses[i] = toTokenResponseHidden(token) // Hide token hash + } + + return c.JSON(http.StatusOK, responses) +} + +// DeleteToken deletes a token by ID +func (h *Handler) DeleteToken(c echo.Context) error { + tokenID := c.Param("id") + if tokenID == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Token ID is required"}) + } + + if err := h.repo.DeleteToken(c.Request().Context(), tokenID); err != nil { + log.Printf("Failed to delete token: %v", err) + return c.JSON(http.StatusInternalServerError, map[string]string{"error": err.Error()}) + } + + return c.JSON(http.StatusOK, map[string]string{"message": "Token deleted successfully"}) +} + +// VerifyToken verifies a token +func (h *Handler) VerifyToken(c echo.Context) error { + var req VerifyTokenRequest + if err := c.Bind(&req); err != nil { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Invalid request"}) + } + + if req.Token == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Token is required"}) + } + + token, err := h.repo.VerifyToken(c.Request().Context(), req.Token, req.UserID, req.OrgID) + if err != nil { + return c.JSON(http.StatusUnauthorized, map[string]string{"error": err.Error()}) + } + + return c.JSON(http.StatusOK, map[string]interface{}{ + "valid": true, + "token": toTokenResponseHidden(token), // Hide token hash in verification response + }) +} + +// GetToken retrieves a token by ID +func (h *Handler) GetToken(c echo.Context) error { + tokenID := c.Param("id") + if tokenID == "" { + return c.JSON(http.StatusBadRequest, map[string]string{"error": "Token ID is required"}) + } + + token, err := h.repo.GetToken(c.Request().Context(), tokenID) + if err != nil { + log.Printf("Failed to get token: %v", err) + return c.JSON(http.StatusNotFound, map[string]string{"error": err.Error()}) + } + + return c.JSON(http.StatusOK, toTokenResponseHidden(token)) // Hide token hash +} + +// HealthCheck returns the health status of the service +func (h *Handler) HealthCheck(c echo.Context) error { + return c.JSON(http.StatusOK, map[string]string{"status": "healthy"}) +} + +// toTokenResponse converts a token model to a response +// Note: Token field will be empty for list/get operations (only shown on creation) +func toTokenResponse(token *types.Token) TokenResponse { + return TokenResponse{ + ID: token.ID, + UserID: token.UserID, + OrgID: token.OrgID, + Token: token.Token, // Will be plaintext only on creation, hash on list/get + Name: token.Name, + Status: token.Status, + CreatedAt: token.CreatedAt, + UpdatedAt: token.UpdatedAt, + LastUsedAt: token.LastUsedAt, + ExpiresAt: token.ExpiresAt, + } +} + +// toTokenResponseHidden converts a token model to a response without showing the token +// Shows last 5 chars of hash for identification (e.g., "abc12") +func toTokenResponseHidden(token *types.Token) TokenResponse { + resp := toTokenResponse(token) + + // Show last 5 chars of hash for identification + if len(token.Token) > 5 { + resp.Token = token.Token[len(token.Token)-5:] + } else { + resp.Token = "" // Empty if hash is too short (shouldn't happen) + } + + return resp +} + diff --git a/taco/internal/token_service/repository.go b/taco/internal/token_service/repository.go new file mode 100644 index 000000000..cd0b7ff0a --- /dev/null +++ b/taco/internal/token_service/repository.go @@ -0,0 +1,168 @@ +package token_service + +import ( + "context" + "crypto/rand" + "crypto/sha256" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "time" + + "github.com/diggerhq/digger/opentaco/internal/query/types" + "gorm.io/gorm" +) + +const ( + errTokenNotFound = "token not found" + queryTokenByID = "id = ?" +) + +// TokenRepository handles token CRUD operations +type TokenRepository struct { + db *gorm.DB +} + +// NewTokenRepository creates a new token repository +func NewTokenRepository(db *gorm.DB) *TokenRepository { + return &TokenRepository{db: db} +} + +// CreateToken creates a new token for a given user ID and org +func (r *TokenRepository) CreateToken(ctx context.Context, userID, orgID, name string, expiresAt *time.Time) (*types.Token, error) { + // Generate secure random token + tokenValue, err := generateSecureToken() + if err != nil { + return nil, fmt.Errorf("failed to generate token: %w", err) + } + + // Hash the token for storage (only hash is stored in DB) + tokenHash := hashToken(tokenValue) + + now := time.Now().UTC() // Always use UTC for consistency + token := &types.Token{ + UserID: userID, + OrgID: orgID, + Token: tokenHash, // Store hash, not plaintext + Name: name, + Status: "active", + CreatedAt: now, + UpdatedAt: now, + ExpiresAt: expiresAt, + } + + if err := r.db.WithContext(ctx).Create(token).Error; err != nil { + return nil, fmt.Errorf("failed to create token: %w", err) + } + + // Return the token with plaintext value (only time user sees it) + // This is safe because the DB stores only the hash + token.Token = tokenValue + return token, nil +} + +// ListTokens returns all tokens for a given user ID and org +func (r *TokenRepository) ListTokens(ctx context.Context, userID, orgID string) ([]*types.Token, error) { + var tokens []*types.Token + query := r.db.WithContext(ctx) + + // Filter by userID if provided + if userID != "" { + query = query.Where("user_id = ?", userID) + } + + // Filter by orgID if provided + if orgID != "" { + query = query.Where("org_id = ?", orgID) + } + + if err := query.Order("created_at DESC").Find(&tokens).Error; err != nil { + return nil, fmt.Errorf("failed to list tokens: %w", err) + } + + return tokens, nil +} + +// DeleteToken deletes a token by ID +func (r *TokenRepository) DeleteToken(ctx context.Context, tokenID string) error { + result := r.db.WithContext(ctx).Delete(&types.Token{}, queryTokenByID, tokenID) + if result.Error != nil { + return fmt.Errorf("failed to delete token: %w", result.Error) + } + if result.RowsAffected == 0 { + return errors.New(errTokenNotFound) + } + return nil +} + +// VerifyToken verifies a token by token value, userID, and orgID +func (r *TokenRepository) VerifyToken(ctx context.Context, tokenValue, userID, orgID string) (*types.Token, error) { + // Hash the provided token to compare with stored hash + tokenHash := hashToken(tokenValue) + + var token types.Token + query := r.db.WithContext(ctx).Where("token = ?", tokenHash) + + if userID != "" { + query = query.Where("user_id = ?", userID) + } + + if orgID != "" { + query = query.Where("org_id = ?", orgID) + } + + if err := query.First(&token).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New(errTokenNotFound) + } + return nil, fmt.Errorf("failed to verify token: %w", err) + } + + // Check if token is active + if token.Status != "active" { + return nil, errors.New("token is not active") + } + + // Check if token has expired + if token.ExpiresAt != nil && time.Now().UTC().After(*token.ExpiresAt) { + return nil, errors.New("token has expired") + } + + // Update last used time asynchronously + go func() { + now := time.Now().UTC() + _ = r.db.Model(&types.Token{}).Where(queryTokenByID, token.ID).Update("last_used_at", now).Error + }() + + return &token, nil +} + +// GetToken retrieves a token by ID +func (r *TokenRepository) GetToken(ctx context.Context, tokenID string) (*types.Token, error) { + var token types.Token + if err := r.db.WithContext(ctx).Where(queryTokenByID, tokenID).First(&token).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, errors.New(errTokenNotFound) + } + return nil, fmt.Errorf("failed to get token: %w", err) + } + return &token, nil +} + +// generateSecureToken generates a cryptographically secure random token +func generateSecureToken() (string, error) { + b := make([]byte, 32) + if _, err := rand.Read(b); err != nil { + return "", err + } + return "otc_tok_" + base64.URLEncoding.EncodeToString(b), nil +} + +// hashToken hashes a token using SHA-256 +// This is a one-way hash - tokens cannot be retrieved from the hash +func hashToken(token string) string { + hash := sha256.Sum256([]byte(token)) + return hex.EncodeToString(hash[:]) +} + diff --git a/taco/internal/token_service/routes.go b/taco/internal/token_service/routes.go new file mode 100644 index 000000000..2b2ccf900 --- /dev/null +++ b/taco/internal/token_service/routes.go @@ -0,0 +1,25 @@ +package token_service + +import ( + "github.com/labstack/echo/v4" +) + +// RegisterRoutes registers all token service routes +func RegisterRoutes(e *echo.Echo, repo *TokenRepository) { + handler := NewHandler(repo) + + // Health check + e.GET("/healthz", handler.HealthCheck) + e.GET("/health", handler.HealthCheck) + + // Token routes under /api/v1/tokens + v1 := e.Group("/api/v1") + + tokens := v1.Group("/tokens") + tokens.POST("", handler.CreateToken) // Create token + tokens.GET("", handler.ListTokens) // List tokens (with query params) + tokens.GET("/:id", handler.GetToken) // Get specific token by ID + tokens.DELETE("/:id", handler.DeleteToken) // Delete token by ID + tokens.POST("/verify", handler.VerifyToken) // Verify token +} + diff --git a/taco/migrations/mysql/20251028185606_20251028115557.sql b/taco/migrations/mysql/20251028185606_20251028115557.sql new file mode 100644 index 000000000..a2ddd8911 --- /dev/null +++ b/taco/migrations/mysql/20251028185606_20251028115557.sql @@ -0,0 +1,17 @@ +-- Create "tokens" table +CREATE TABLE `tokens` ( + `id` varchar(36) NOT NULL, + `user_id` varchar(255) NOT NULL, + `org_id` varchar(255) NOT NULL, + `token` varchar(255) NOT NULL, + `name` varchar(255) NULL, + `status` varchar(20) NULL DEFAULT "active", + `created_at` datetime NULL, + `updated_at` datetime NULL, + `last_used_at` datetime NULL, + `expires_at` datetime NULL, + PRIMARY KEY (`id`), + INDEX `idx_tokens_org_id` (`org_id`), + UNIQUE INDEX `idx_tokens_token` (`token`), + INDEX `idx_tokens_user_id` (`user_id`) +) CHARSET utf8mb4 COLLATE utf8mb4_0900_ai_ci; diff --git a/taco/migrations/postgres/20251028185601_20251028115557.sql b/taco/migrations/postgres/20251028185601_20251028115557.sql new file mode 100644 index 000000000..c934f2526 --- /dev/null +++ b/taco/migrations/postgres/20251028185601_20251028115557.sql @@ -0,0 +1,20 @@ +-- Create "tokens" table +CREATE TABLE "public"."tokens" ( + "id" character varying(36) NOT NULL, + "user_id" character varying(255) NOT NULL, + "org_id" character varying(255) NOT NULL, + "token" character varying(255) NOT NULL, + "name" character varying(255) NULL, + "status" character varying(20) NULL DEFAULT 'active', + "created_at" timestamptz NULL, + "updated_at" timestamptz NULL, + "last_used_at" timestamptz NULL, + "expires_at" timestamptz NULL, + PRIMARY KEY ("id") +); +-- Create index "idx_tokens_org_id" to table: "tokens" +CREATE INDEX "idx_tokens_org_id" ON "public"."tokens" ("org_id"); +-- Create index "idx_tokens_token" to table: "tokens" +CREATE UNIQUE INDEX "idx_tokens_token" ON "public"."tokens" ("token"); +-- Create index "idx_tokens_user_id" to table: "tokens" +CREATE INDEX "idx_tokens_user_id" ON "public"."tokens" ("user_id"); diff --git a/taco/migrations/sqlite/20251028185607_20251028115557.sql b/taco/migrations/sqlite/20251028185607_20251028115557.sql new file mode 100644 index 000000000..2275acf1f --- /dev/null +++ b/taco/migrations/sqlite/20251028185607_20251028115557.sql @@ -0,0 +1,20 @@ +-- Create "tokens" table +CREATE TABLE `tokens` ( + `id` varchar NULL, + `user_id` varchar NOT NULL, + `org_id` varchar NOT NULL, + `token` varchar NOT NULL, + `name` varchar NULL, + `status` varchar NULL DEFAULT 'active', + `created_at` datetime NULL, + `updated_at` datetime NULL, + `last_used_at` datetime NULL, + `expires_at` datetime NULL, + PRIMARY KEY (`id`) +); +-- Create index "idx_tokens_token" to table: "tokens" +CREATE UNIQUE INDEX `idx_tokens_token` ON `tokens` (`token`); +-- Create index "idx_tokens_org_id" to table: "tokens" +CREATE INDEX `idx_tokens_org_id` ON `tokens` (`org_id`); +-- Create index "idx_tokens_user_id" to table: "tokens" +CREATE INDEX `idx_tokens_user_id` ON `tokens` (`user_id`);