wp_update_post does not change post status from draft to publish, returns 1

Something about wp_update_post doesn’t like to be called by AJAX.

No, there is no such thing. 🙂

My suspicion is it has something to do with security

And I believed that also wasn’t the case, hence I asked for your JS code.

And the actual problem is that the POST parameter postid (i.e. $_POST['postid']) is actually an array of post IDs, so you can’t simply do 'ID' => $_POST['postid']. And regarding this: “wp_update_post returns a 1“, it’s because the function calls get_post() and it’s actually the one which returned 1. So that also means, the post that was updated is the one with that ID, i.e. 1 (which is the “Hello world!” post in a default WordPress install).

So you need to do a foreach then call wp_update_post() to update each of the posts. Example based on your code:

function approvepost_callback() {
    $data = array();

    foreach ( (array) $_POST['postid'] as $post_id ) {
        if ( empty( $post_id ) || ! is_numeric( $post_id ) ) {
            continue;
        }

        $post = array(
            'ID'          => $post_id,
            'post_status' => 'publish',
            'edit_date'   => true // what is edit_date?
        );

        $result = wp_update_post( $post, true );
        if ( $result && ! is_wp_error( $result ) ) {
            $data[] = $post_id;
        }
    }

    // In your JS, you set dataType to 'json', so make sure to send a valid JSON response.
    wp_send_json( $data ); // this function will call wp_die()
}

Some JavaScript Notes

  1. You don’t need the following else because it will add empty items to the array (data.postid):

    else{
        data.postid[i] = "";
    }
    
  2. I would ensure the above array is not empty before proceeding with the AJAX ($.ajax()) call:

    if ( data.postid.length < 1 ) {
        console.log( 'no selections' );
        return;
    }
    
    $.ajax( ... your code here. );
    

And last but not least… you should consider using the REST API instead to update your posts (see the Update a Post endpoint), because among other good things, the REST API has a better error handling and for example if you specified an invalid/non-existent post ID, the response would look like so which tells what exactly went wrong: {code: "rest_post_invalid_id", message: "Invalid post ID.", data: {status: 404}} — better than simply 0 or 1, right? 🙂