Skip to content

[Bug]: Cannot update Luxon Datetime #1782

@AndreRidho

Description

@AndreRidho

Contact Details

[email protected]

What happened?

Trying to implement a basic CRUD admin dashboard in my AdonisJS Typescript server. When attempting to update objects in DB and the update contains a datetime, an error occurs. The issue does not occur if the update's datetime is empty. The path for the below screenshot is http://localhost:3333/admin/resources/proposals/actions/new

Image

Bug prevalence

Whenever I try to update datetime

AdminJS dependencies version

{
"name": "myproject",
"version": "0.0.0",
"private": true,
"type": "module",
"license": "UNLICENSED",
"scripts": {
"start": "node bin/server.js",
"build": "node ace build",
"dev": "node ace serve --watch",
"test": "node ace test",
"lint": "eslint .",
"format": "prettier --write .",
"typecheck": "tsc --noEmit"
},
"imports": {
"#controllers/": "./app/controllers/.js",
"#exceptions/": "./app/exceptions/.js",
"#models/": "./app/models/.js",
"#mails/": "./app/mails/.js",
"#services/": "./app/services/.js",
"#listeners/": "./app/listeners/.js",
"#events/": "./app/events/.js",
"#middleware/": "./app/middleware/.js",
"#validators/": "./app/validators/.js",
"#providers/": "./providers/.js",
"#policies/": "./app/policies/.js",
"#abilities/": "./app/abilities/.js",
"#database/": "./database/.js",
"#start/": "./start/.js",
"#tests/": "./tests/.js",
"#config/": "./config/.js"
},
"devDependencies": {
"@adonisjs/assembler": "^7.7.0",
"@adonisjs/eslint-config": "^1.3.0",
"@adonisjs/prettier-config": "^1.3.0",
"@adonisjs/tsconfig": "^1.3.0",
"@japa/api-client": "^2.0.3",
"@japa/assert": "^3.0.0",
"@japa/plugin-adonisjs": "^3.0.1",
"@japa/runner": "^3.1.4",
"@swc/core": "^1.6.5",
"@types/jsonwebtoken": "^9.0.10",
"@types/luxon": "^3.6.2",
"@types/node": "^20.14.9",
"@types/ws": "^8.18.1",
"eslint": "^8.57.0",
"hot-hook": "^0.2.6",
"nodemon": "^3.1.10",
"pino-pretty": "^11.2.1",
"prettier": "^3.3.2",
"ts-node": "^10.9.2",
"typescript": "~5.4"
},
"dependencies": {
"@adminjs/adonis": "^1.1.1",
"@adminjs/express": "^6.1.1",
"@adonisjs/auth": "^9.2.3",
"@adonisjs/core": "^6.12.1",
"@adonisjs/cors": "^2.2.1",
"@adonisjs/lucid": "^21.7.0",
"@adonisjs/session": "^7.5.1",
"@adonisjs/websocket": "^1.0.12",
"@vinejs/vine": "^2.1.0",
"adminjs": "^7.8.17",
"adonis-lucid-filter": "^5.2.0",
"axios": "^1.10.0",
"express": "^5.1.0",
"jsonwebtoken": "^9.0.2",
"luxon": "^3.7.1",
"mysql2": "^3.14.2",
"reflect-metadata": "^0.2.2",
"socket.io": "^4.8.1",
"stripe": "^18.3.0",
"ws": "^8.18.3"
},
"hotHook": {
"boundaries": [
"./app/controllers/**/.ts",
"./app/middleware/
.ts"
]
},
"eslintConfig": {
"extends": "@adonisjs/eslint-config/app"
},
"prettier": "@adonisjs/prettier-config"
}

What browsers do you see the problem on?

Chrome

Relevant log output

[13:38:39.581] ERROR (8800): Invalid value for "Proposal.updatedAt". It must be an instance of "luxon.DateTime"
    request_id: "qf2sjvyzzq2ddv2xdkfwl1j5"
    x-request-id: "qf2sjvyzzq2ddv2xdkfwl1j5"
    err: {
      "type": "",
      "message": "Invalid value for \"Proposal.updatedAt\". It must be an instance of \"luxon.DateTime\"",
      "stack":
          Exception: Invalid value for "Proposal.updatedAt". It must be an instance of "luxon.DateTime"
              at Object.prepareDateTimeColumn [as prepare] (file:///C:/myproject/node_modules/@adonisjs/lucid/build/src/orm/decorators/date_time.js:41:11)   
              at file:///C:/myproject/node_modules/@adonisjs/lucid/build/src/orm/base_model/index.js:847:26
              at Array.reduce (<anonymous>)
              at Proxy.prepareForAdapter (file:///C:/myproject/node_modules/@adonisjs/lucid/build/src/orm/base_model/index.js:844:40)
              at Proxy.save (file:///C:/myproject/node_modules/@adonisjs/lucid/build/src/orm/base_model/index.js:1419:52)
              at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
              at Resource2.create (C:\myproject\node_modules\@adminjs\adonis\src\adapter\resource.ts:127:5)
              at async BaseRecord.create (file:///C:/myproject/node_modules/adminjs/lib/backend/adapters/record/base-record.js:191:30)
              at async Object.handler (file:///C:/myproject/node_modules/adminjs/lib/backend/actions/new/new-action.js:37:16)
              at async ActionDecorator.handler (file:///C:/myproject/node_modules/adminjs/lib/backend/decorators/action/action-decorator.js:56:19)
              at Object.handler (C:\myproject\node_modules\@adminjs\adonis\src\plugin\router.ts:130:28)
              at SessionMiddleware.handle (C:\myproject\node_modules\@adonisjs\session\src\session_middleware.ts:81:22)
      "name": "Exception",
      "status": 500,
      "code": "E_INVALID_DATE_COLUMN_VALUE"
    }

// Possibly unrelated, but also getting many duplicate logs of the following message:

Unexpected type: int unsigned fallback to string

Relevant code that's giving you issues

// adonisrc.ts:

import { defineConfig } from '@adonisjs/core/app'

export default defineConfig({
  /*
  |--------------------------------------------------------------------------
  | Commands
  |--------------------------------------------------------------------------
  |
  | List of ace commands to register from packages. The application commands
  | will be scanned automatically from the "./commands" directory.
  |
  */
  commands: [
    () => import('@adonisjs/core/commands'),
    () => import('@adonisjs/lucid/commands'),
    () => import('adonis-lucid-filter/commands'),
  ],

  /*
  |--------------------------------------------------------------------------
  | Service providers
  |--------------------------------------------------------------------------
  |
  | List of service providers to import and register when booting the
  | application
  |
  */
  providers: [
    () => import('@adonisjs/core/providers/app_provider'),
    () => import('@adonisjs/core/providers/hash_provider'),
    {
      file: () => import('@adonisjs/core/providers/repl_provider'),
      environment: ['repl', 'test'],
    },
    () => import('@adonisjs/core/providers/vinejs_provider'),
    () => import('@adonisjs/cors/cors_provider'),
    () => import('@adonisjs/lucid/database_provider'),
    () => import('@adonisjs/auth/auth_provider'),
    () => import('adonis-lucid-filter/provider'),
    () => import('@adonisjs/session/session_provider'),
    {
      file: () => import('@adminjs/adonis/adminjs_provider'),
      environment: ['web'],
    },
    () => import('./app/providers/socket_provider.js'),
  ],

  /*
  |--------------------------------------------------------------------------
  | Preloads
  |--------------------------------------------------------------------------
  |
  | List of modules to import before starting the application.
  |
  */
  preloads: [
    () => import('#start/routes'),
    () => import('#start/kernel'),
  ],

  /*
  |--------------------------------------------------------------------------
  | Tests
  |--------------------------------------------------------------------------
  |
  | List of test suites to organize tests by their type. Feel free to remove
  | and add additional suites.
  |
  */
  tests: {
    suites: [
      {
        files: ['tests/unit/**/*.spec(.ts|.js)'],
        name: 'unit',
        timeout: 2000,
      },
      {
        files: ['tests/functional/**/*.spec(.ts|.js)'],
        name: 'functional',
        timeout: 30000,
      },
    ],
    forceExit: false,
  },
})

// app/admin/auth.ts:

import { CurrentAdmin, DefaultAuthProvider, DefaultAuthenticatePayload } from 'adminjs'
import componentLoader from './component_loader.js'
import User from '#models/user'
import hash from '@adonisjs/core/services/hash'

const authenticate = async ({
  email,
  password,
}: DefaultAuthenticatePayload): Promise<CurrentAdmin | null> => {
  const user = await User.findBy('email', email)

  if (!user) {
    return null // not found
  }

  const isValid = await hash.verify(user.password, password)

  if (!isValid) {
    return null // invalid password
  }

  return {
    email: user.email,
    id: user.id.toString(),
  }
}

const authProvider = new DefaultAuthProvider({
  componentLoader,
  authenticate,
})

export default authProvider

// app/admin/component_loader.ts:

import { ComponentLoader } from 'adminjs'

const componentLoader = new ComponentLoader()

export default componentLoader

// config/adminjs.ts:

import { AdminJSProviderConfig, LucidResource } from '@adminjs/adonis'
import {} from '@adminjs/adonis'
import componentLoader from '../app/admin/component_loader.js'
import authProvider from '../app/admin/auth.js'
import Answer from '#models/answer'
import Category from '#models/category'
import Choice from '#models/choice'
import Customer from '#models/customer'
import Job from '#models/job'
import Payment from '#models/payment'
import Professional from '#models/professional'
import Proposal from '#models/proposal'
import Question from '#models/question'
import Service from '#models/service'
import User from '#models/user'
import Warning from '#models/warning'
import FavoritedService from '#models/favorited_service'
import ServiceAllowedProfessionalTypes from '#models/service_allowed_professional_types'

const adminjsConfig: AdminJSProviderConfig = {
  adapter: {
    enabled: true,
  },
  adminjs: {
    rootPath: '/admin',
    loginPath: '/admin/login',
    logoutPath: '/admin/logout',
    componentLoader,
    resources: [
      new LucidResource(Answer, 'mysql'),
      new LucidResource(Category, 'mysql'),
      new LucidResource(Choice, 'mysql'),
      new LucidResource(Customer, 'mysql'),
      new LucidResource(FavoritedService, 'mysql'),
      new LucidResource(Job, 'mysql'),
      new LucidResource(Payment, 'mysql'),
      new LucidResource(Professional, 'mysql'),
      new LucidResource(Proposal, 'mysql'),
      new LucidResource(Question, 'mysql'),
      new LucidResource(Service, 'mysql'),
      new LucidResource(ServiceAllowedProfessionalTypes, 'mysql'),
      new LucidResource(User, 'mysql'),
      new LucidResource(Warning, 'mysql'),
    ],
    pages: {},
    locale: {
      availableLanguages: ['en'],
      language: 'en',
      translations: {
        en: {
          actions: {},
          messages: {},
          labels: {},
          buttons: {},
          properties: {},
          components: {},
          pages: {},
          ExampleResource: {
            actions: {},
            messages: {},
            labels: {},
            buttons: {},
            properties: {},
          },
        },
      },
    },
    branding: {
      companyName: 'AdminJS',
      theme: {},
    },
    settings: {
      defaultPerPage: 10,
    },
  },
  auth: {
    enabled: true,
    provider: authProvider,
    middlewares: [],
  },
  middlewares: [],
}

export default adminjsConfig

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions